एक्सआर के लिए, Jetpack Compose की मदद से स्पेशल यूज़र इंटरफ़ेस (यूआई) बनाना

XR डिवाइसों के लिए उपलब्ध सुविधाएं
इस गाइड की मदद से, इन XR डिवाइसों के लिए अनुभव बनाए जा सकते हैं.
XR हेडसेट
वायर वाले XR चश्मे

XR के लिए Jetpack Compose की मदद से, रो और कॉलम जैसे Compose के जाने-पहचाने कॉन्सेप्ट का इस्तेमाल करके, अपने स्पेशल यूज़र इंटरफ़ेस (यूआई) और लेआउट को डिक्लेरेटिव तरीके से बनाया जा सकता है. इससे, मौजूदा Android यूज़र इंटरफ़ेस (यूआई) को 3D स्पेस में बढ़ाया जा सकता है या पूरी तरह से नए इमर्सिव 3D ऐप्लिकेशन बनाए जा सकते हैं.

अगर मौजूदा Android Views पर आधारित किसी ऐप्लिकेशन को स्पेशलाइज़ किया जा रहा है, तो आपके पास डेवलपमेंट के कई विकल्प होते हैं. इंटरऑपरेबिलिटी एपीआई का इस्तेमाल किया जा सकता है. साथ ही, Compose और Views का एक साथ इस्तेमाल किया जा सकता है या सीधे SceneCore लाइब्रेरी के साथ काम किया जा सकता है. ज़्यादा जानकारी के लिए, व्यू के साथ काम करने के बारे में हमारी गाइड देखें.

सबस्पेस और स्पेशलाइज़ किए गए कॉम्पोनेंट के बारे में जानकारी

Android XR के लिए अपना ऐप्लिकेशन लिखते समय, सबस्पेस और स्पेशलाइज़ किए गए कॉम्पोनेंट के कॉन्सेप्ट को समझना ज़रूरी है.

सबस्पेस के बारे में जानकारी

Android XR के लिए डेवलपमेंट करते समय, आपको अपने ऐप्लिकेशन या लेआउट में एक सबस्पेस जोड़ना होगा. सबस्पेस, आपके ऐप्लिकेशन में 3D स्पेस का एक हिस्सा होता है. इसमें 3D कॉन्टेंट रखा जा सकता है, 3D लेआउट बनाए जा सकते हैं, और 2D कॉन्टेंट में डेप्थ जोड़ी जा सकती है. सबस्पेस सिर्फ़ तब रेंडर होता है, जब स्पेशलाइज़ेशन की सुविधा चालू होती है. होम स्पेस या XR डिवाइसों के अलावा अन्य डिवाइसों पर, उस सबस्पेस में मौजूद किसी भी कोड को अनदेखा कर दिया जाता है.

सबस्पेस बनाने के कुछ तरीके यहां दिए गए हैं:

  • Subspace: यह कंपोज़ेबल, नया और अलग स्पेशल यूज़र इंटरफ़ेस (यूआई) हैरारकी बनाता है. यह, नेस्ट किए गए किसी भी पैरंट Subspace की स्पेशल पोज़िशन, ओरिएंटेशन या स्केल को इनहेरिट नहीं करता. Subspace को, सिस्टम के सुझाए गए कॉन्टेंट बॉक्स से अपने-आप बाइंड कर दिया जाता है.
  • PlanarEmbeddedSubspace: इस कंपोज़ेबल को, आपके ऐप्लिकेशन की यूज़र इंटरफ़ेस (यूआई) हैरारकी में रखा जा सकता है. इससे, 2D और स्पेशल यूज़र इंटरफ़ेस (यूआई) के लिए लेआउट बनाए जा सकते हैं. PlanarEmbeddedSubspace , अपने पैरंट की कंस्ट्रेंट और पोज़िशनिंग का पालन करता है. इसके अंदर रखा गया 3D कॉन्टेंट, इस 2D-डिफ़ाइन किए गए एरिया के हिसाब से पोज़िशन किया जाता है.

ज़्यादा जानकारी के लिए, अपने ऐप्लिकेशन में सबस्पेस जोड़ना लेख पढ़ें.

स्पेशलाइज़ किए गए कॉम्पोनेंट के बारे में जानकारी

सबस्पेस कंपोज़ेबल: इन कॉम्पोनेंट को सिर्फ़ सबस्पेस में रेंडर किया जा सकता है. इन्हें 2D लेआउट में रखने से पहले, Subspace में शामिल करना ज़रूरी है. SubspaceModifier की मदद से, अपने सबस्पेस कंपोज़ेबल में डेप्थ, ऑफ़सेट, और पोज़िशनिंग जैसे एट्रिब्यूट जोड़े जा सकते हैं.

अन्य स्पेशलाइज़ किए गए कॉम्पोनेंट को सबस्पेस में कॉल करने की ज़रूरत नहीं होती. इनमें, स्पेशल कंटेनर में रैप किए गए सामान्य 2D एलिमेंट शामिल होते हैं. अगर इन एलिमेंट को 2D और 3D, दोनों लेआउट के लिए तय किया गया है, तो इन्हें दोनों में इस्तेमाल किया जा सकता है. स्पेशलाइज़ेशन की सुविधा चालू न होने पर, इनकी स्पेशलाइज़ की गई सुविधाओं को अनदेखा कर दिया जाएगा. साथ ही, ये अपने 2D वर्शन में वापस आ जाएंगे.

स्पेशल पैनल बनाना

A SpatialPanel एक सबस्पेस कंपोज़ेबल है. इसकी मदद से, ऐप्लिकेशन का कॉन्टेंट दिखाया जा सकता है. उदाहरण के लिए, वीडियो प्लेबैक, स्थिर इमेज या कोई अन्य कॉन्टेंट, स्पेशल पैनल में दिखाया जा सकता है.

स्पेशल यूज़र इंटरफ़ेस (यूआई) पैनल का उदाहरण

SubspaceModifier का इस्तेमाल करके, स्पेशल पैनल का साइज़, व्यवहार, और पोज़िशनिंग बदली जा सकती है. जैसा कि यहां दिए गए उदाहरण में दिखाया गया है.

Subspace {
    SpatialPanel(
        SubspaceModifier
            .height(824.dp)
            .width(1400.dp),
        dragPolicy = MovePolicy(),
        resizePolicy = ResizePolicy(),
    ) {
        SpatialPanelContent()
    }
}

@Composable
fun SpatialPanelContent() {
    Box(
        Modifier
            .background(color = Color.Black)
            .height(500.dp)
            .width(500.dp),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = "Spatial Panel",
            color = Color.White,
            fontSize = 25.sp
        )
    }
}

कोड के बारे में अहम बातें

  • SpatialPanel के एपीआई, सबस्पेस कंपोज़ेबल होते हैं. इसलिए, इन्हें Subspace में कॉल करना ज़रूरी है. इन्हें सबस्पेस के बाहर कॉल करने पर, एक अपवाद दिखता है.
  • SpatialPanel का साइज़, SubspaceModifier पर height और width की खास जानकारी का इस्तेमाल करके सेट किया गया है. इन खास जानकारी को शामिल न करने पर, पैनल का साइज़ उसके कॉन्टेंट के मेज़रमेंट से तय होता है.
  • movable सबस्पेस मॉडिफ़ायर जोड़कर, उपयोगकर्ता को पैनल को मूव करने की अनुमति दें.
  • resizable सबस्पेस मॉडिफ़ायर जोड़कर, उपयोगकर्ता को पैनल का साइज़ बदलने की अनुमति दें.
  • साइज़ और पोज़िशनिंग के बारे में जानकारी पाने के लिए, हमारे स्पेशल पैनल के डिज़ाइन से जुड़े दिशा-निर्देश देखें. कोड लागू करने के बारे में ज़्यादा जानकारी पाने के लिए, हमारा रेफ़रंस दस्तावेज़ देखें.

movable मॉडिफ़ायर कैसे काम करता है

जब कोई उपयोगकर्ता किसी पैनल को अपने से दूर ले जाता है, तो डिफ़ॉल्ट रूप से, movable मॉडिफ़ायर उस पैनल को उसी तरह से स्केल करता है जिस तरह से सिस्टम, होम स्पेस में पैनल का साइज़ बदलता है. सभी चाइल्ड कॉन्टेंट, इस व्यवहार को इनहेरिट करते हैं. इसे बंद करने के लिए, shouldScaleWithDistance पैरामीटर को false पर सेट करें.

ऑर्बिटर बनाना

ऑर्बिटर, स्पेशल यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट होता है. इसे, मिलते-जुलते स्पेशल पैनल या स्पेशल लेआउट कॉम्पोनेंट से अटैच करने के लिए डिज़ाइन किया गया है. जैसे, SpatialColumn, SpatialRow या SpatialBox. ऑर्बिटर में आम तौर पर, नेविगेशन और कॉन्टेक्चुअल ऐक्शन आइटम शामिल होते हैं. ये आइटम, उस एंटिटी से जुड़े होते हैं जिससे इसे ऐंकर किया गया है. उदाहरण के लिए, अगर आपने वीडियो कॉन्टेंट दिखाने के लिए कोई स्पेशल पैनल बनाया है, तो ऑर्बिटर में वीडियो प्लेबैक कंट्रोल जोड़े जा सकते हैं.

ऑर्बिटर का उदाहरण

यहां दिए गए उदाहरण में दिखाए गए तरीके से, SpatialPanel में 2D लेआउट के अंदर ऑर्बिटर को कॉल करें, ताकि नेविगेशन जैसे उपयोगकर्ता के कंट्रोल को रैप किया जा सके. ऐसा करने से, ये कंट्रोल आपके 2D लेआउट से एक्सट्रैक्ट हो जाते हैं और आपकी कॉन्फ़िगरेशन के मुताबिक, स्पेशल पैनल से अटैच हो जाते हैं.

Subspace {
    SpatialPanel(
        SubspaceModifier
            .height(824.dp)
            .width(1400.dp),
        dragPolicy = MovePolicy(),
        resizePolicy = ResizePolicy(),
    ) {
        SpatialPanelContent()
        OrbiterExample()
    }
}

@Composable
fun OrbiterExample() {
    Orbiter(
        position = ContentEdge.Bottom,
        offset = 96.dp,
        alignment = Alignment.CenterHorizontally
    ) {
        Surface(Modifier.clip(CircleShape)) {
            Row(
                Modifier
                    .background(color = Color.Black)
                    .height(100.dp)
                    .width(600.dp),
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    text = "Orbiter",
                    color = Color.White,
                    fontSize = 50.sp
                )
            }
        }
    }
}

कोड के बारे में अहम बातें

  • ऑर्बिटर, स्पेशल यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट होते हैं. इसलिए, कोड को 2D या 3D लेआउट में फिर से इस्तेमाल किया जा सकता है. 2D लेआउट में, आपका ऐप्लिकेशन सिर्फ़ ऑर्बिटर में मौजूद कॉन्टेंट को रेंडर करता है और ऑर्बिटर को अनदेखा कर देता है.
  • ऑर्बिटर का इस्तेमाल और डिज़ाइन करने के बारे में ज़्यादा जानकारी पाने के लिए, हमारे डिज़ाइन से जुड़े दिशा-निर्देश देखें.

स्पेशल लेआउट में एक से ज़्यादा स्पेशल पैनल जोड़ना

एक से ज़्यादा स्पेशल पैनल बनाए जा सकते हैं और उन्हें स्पेशल लेआउट में रखा जा सकता है using SpatialRow, SpatialColumn, SpatialBox, और SpatialSpacer.

स्पेशल लेआउट में एक से ज़्यादा स्पेशल पैनल का उदाहरण

यहां दिए गए कोड के उदाहरण में, ऐसा करने का तरीका बताया गया है.

Subspace {
    SpatialRow {
        SpatialColumn {
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Top Left")
            }
            SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) {
                SpatialPanelContent("Middle Left")
            }
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Bottom Left")
            }
        }
        SpatialColumn {
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Top Right")
            }
            SpatialPanel(SubspaceModifier.height(200.dp).width(400.dp)) {
                SpatialPanelContent("Middle Right")
            }
            SpatialPanel(SubspaceModifier.height(250.dp).width(400.dp)) {
                SpatialPanelContent("Bottom Right")
            }
        }
    }
}

@Composable
fun SpatialPanelContent(text: String) {
    Column(
        Modifier
            .background(color = Color.Black)
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Panel",
            color = Color.White,
            fontSize = 15.sp
        )
        Text(
            text = text,
            color = Color.White,
            fontSize = 25.sp,
            fontWeight = FontWeight.Bold
        )
    }
}

कोड के बारे में अहम बातें

  • SpatialRow, SpatialColumn, SpatialBox, और SpatialSpacer सभी सबस्पेस कंपोज़ेबल हैं. इन्हें सबस्पेस में रखना ज़रूरी है.
  • अपने लेआउट को पसंद के मुताबिक बनाने के लिए, SubspaceModifier का इस्तेमाल करें.
  • एक लाइन में कई पैनल वाले लेआउट के लिए, हमारा सुझाव है कि SubspaceModifier का इस्तेमाल करके, 825dp का कर्व रेडियस सेट करें, ताकि पैनल आपके उपयोगकर्ता को घेर सकें. ज़्यादा जानकारी के लिए, हमारे डिज़ाइन से जुड़े दिशा-निर्देश देखें.

SpatialGltfModel का इस्तेमाल करके, अपने लेआउट में 3D ऑब्जेक्ट जोड़ना

Android XR, 3D मॉडल के लिए glTF फ़ॉर्मैट के साथ काम करता है. आम तौर पर, इन्हें .glb फ़ाइलों के तौर पर सेव किया जाता है. इन ऑब्जेक्ट को अपने लेआउट में जोड़ने के लिए, SpatialGltfModel कंपोज़ेबल का इस्तेमाल करना चाहिए. इस एपीआई की मदद से, ऐसेट लोड करने और उनकी स्थिति को मैनेज करने की प्रोसेस आसान हो जाती है.

किसी मॉडल को दिखाने के लिए, पहले उसके सोर्स और स्थिति को तय करने के लिए rememberSpatialGltfModelState का इस्तेमाल करें. मॉडल को अपने ऐप्लिकेशन के assets फ़ोल्डर, URI या raw data से लोड किया जा सकता है.

val modelState = rememberSpatialGltfModelState(
    source = SpatialGltfModelSource.fromPath(
        Paths.get("models/model_name.glb")
    )
)

स्थिति तय हो जाने के बाद, SpatialGltfModel कंपोज़ेबल का इस्तेमाल करके, उसे सबस्पेस में रेंडर करें.

SpatialGltfModel(state = modelState, modifier = SubspaceModifier)

कोड के बारे में अहम बातें

  • एसिंक्रोनस लोडिंग: मॉडल को एसिंक्रोनस तरीके से लोड किया जाता है. शुरुआती कंपोज़िशन के दौरान, इसका इंट्रिंसिक साइज़ शून्य हो सकता है. मॉडल तैयार होने के बाद, लेआउट फिर से मेज़र होता है.
  • स्थिति को कंट्रोल करना: लोडिंग की स्थिति के बारे में क्वेरी करने या ऐनिमेशन को कंट्रोल करने के लिए, SpatialGltfModelState.status का इस्तेमाल करें.
  • साइज़ और स्केलिंग: डिफ़ॉल्ट रूप से, लेआउट का साइज़, ऐसेट के बाउंडिंग बॉक्स के साइज़ के बराबर होता है. मॉडल को तय की गई सीमाओं के अंदर फ़िट करने के लिए, SubspaceModifier.size का इस्तेमाल करके, इसे बदला जा सकता है.

अपने लेआउट में एंटिटी रखने के लिए, SceneCoreEntity का इस्तेमाल करना

SceneCoreEntity कंपोज़ेबल, Jetpack SceneCore और XR के लिए Compose लाइब्रेरी के बीच ब्रिज का काम करता है. इससे, Compose लेआउट में SceneCore की मदद से बनाई गई एंटिटी का इस्तेमाल किया जा सकता है. इससे, लोअर-लेवल एंटिटी और कस्टम कॉम्पोनेंट बनाए जा सकते हैं. साथ ही, Compose को उन एंटिटी का साइज़ तय करने, उन्हें पोज़िशन करने, उन्हें फिर से पैरंट करने, उनमें चाइल्ड जोड़ने, और उन पर मॉडिफ़ायर लागू करने की अनुमति मिलती है.

Subspace {
    SceneCoreEntity(
        modifier = SubspaceModifier.offset(x = 50.dp),
        factory = {
            SurfaceEntity.create(
                session = session,
                pose = Pose.Identity,
                stereoMode = SurfaceEntity.StereoMode.MONO
            )
        },
        update = { entity ->
            // compose state changes may be applied to the
            // SceneCore entity here.
            entity.stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE
        },
        sizeAdapter =
            SceneCoreEntitySizeAdapter({
                IntSize2d(it.width, it.height)
            }),
    ) {
        // Content here will be children of the SceneCoreEntity
        // in the scene graph.
    }
}

कोड के बारे में अहम बातें

  • फ़ैक्ट्री ब्लॉक: फ़ैक्ट्री ब्लॉक वह जगह होती है जहां, आपने SceneCore की बुनियादी एंटिटी को शुरू किया है.
  • अपडेट ब्लॉक: अपने Compose की स्थिति में हुए बदलावों के जवाब में, एंटिटी की प्रॉपर्टी में बदलाव करने के लिए, अपडेट ब्लॉक का इस्तेमाल करें.
  • साइज़ अडैप्टेशन: sizeAdapter, एंटिटी के डाइमेंशन को Compose लेआउट सिस्टम पर वापस भेजता है.

अतिरिक्त जानकारी

इमेज या वीडियो कॉन्टेंट के लिए कोई सर्फ़ेस जोड़ना

A SpatialExternalSurface एक सबस्पेस कंपोज़ेबल है. यह Surface बनाता और मैनेज करता है. इस Surface में, आपका ऐप्लिकेशन कॉन्टेंट दिखा सकता है. जैसे, कोई इमेज या वीडियो. SpatialExternalSurface , स्टीरियोस्कोपिक या मोनोस्कोपिक, दोनों तरह के कॉन्टेंट के साथ काम करता है.

इस उदाहरण में, साइड-बाय-साइड स्टीरियोस्कोपिक वीडियो लोड करने का तरीका बताया गया है, जिसमें Media3 Exoplayer और SpatialExternalSurface का इस्तेमाल किया गया है:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun SpatialExternalSurfaceContent() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp) // Default width is 400.dp if no width modifier is specified
                .height(676.dp), // Default height is 400.dp if no height modifier is specified
            // Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
            // upon which type of content you are rendering: monoscopic content, side-by-side stereo
            // content, or top-bottom stereo content
            stereoMode = StereoMode.SideBySide,
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }
            val videoUri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                // Represents a side-by-side stereo video, where each frame contains a pair of
                // video frames arranged side-by-side. The frame on the left represents the left
                // eye view, and the frame on the right represents the right eye view.
                .path("sbs_video.mp4")
                .build()
            val mediaItem = MediaItem.fromUri(videoUri)

            // onSurfaceCreated is invoked only one time, when the Surface is created
            onSurfaceCreated { surface ->
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }
            // onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
            // associated Surface are destroyed
            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

कोड के बारे में अहम बातें

  • रेंडर किए जा रहे कॉन्टेंट के टाइप के हिसाब से, StereoMode को Mono, SideBySide या TopBottom पर सेट करें:
    • Mono: इमेज या वीडियो फ़्रेम में एक ही इमेज होती है, जो दोनों आंखों को दिखती है.
    • SideBySide: इमेज या वीडियो फ़्रेम में, साइड-बाय-साइड व्यवस्थित की गई इमेज या वीडियो फ़्रेम का पेयर होता है. इसमें, बाईं ओर की इमेज या फ़्रेम, बाईं आंख के व्यू को दिखाता है. वहीं, दाईं ओर की इमेज या फ़्रेम, दाईं आंख के व्यू को दिखाता है.
    • TopBottom: इमेज या वीडियो फ़्रेम में, वर्टिकल तरीके से स्टैक की गई इमेज या वीडियो फ़्रेम का पेयर होता है. इसमें, ऊपर की इमेज या फ़्रेम, बाईं आंख के व्यू को दिखाता है. वहीं, नीचे की इमेज या फ़्रेम, दाईं आंख के व्यू को दिखाता है.
  • SpatialExternalSurface सिर्फ़ रेक्टैंगुलर सर्फ़ेस के साथ काम करता है.
  • यह Surface, इनपुट इवेंट कैप्चर नहीं करता.
  • StereoMode में किए गए बदलावों को, ऐप्लिकेशन रेंडरिंग या वीडियो डिकोडिंग के साथ सिंक नहीं किया जा सकता.
  • यह कंपोज़ेबल, अन्य पैनल के सामने रेंडर नहीं हो सकता. इसलिए, अगर लेआउट में अन्य पैनल मौजूद हैं, तो MovePolicy का इस्तेमाल नहीं करना चाहिए.

DRM से सुरक्षित वीडियो कॉन्टेंट के लिए कोई सर्फ़ेस जोड़ना

SpatialExternalSurface , DRM से सुरक्षित वीडियो स्ट्रीम के प्लेबैक के साथ भी काम करता है. इसे चालू करने के लिए, आपको एक सुरक्षित सर्फ़ेस बनाना होगा, जो सुरक्षित ग्राफ़िक्स बफ़र पर रेंडर होता है. इससे, कॉन्टेंट को स्क्रीन-रिकॉर्ड होने या असुरक्षित सिस्टम कॉम्पोनेंट से ऐक्सेस होने से रोका जा सकता है.

सुरक्षित सर्फ़ेस बनाने के लिए, surfaceProtection पैरामीटर को SurfaceProtection.Protected पर SpatialExternalSurface कंपोज़ेबल पर सेट करें. इसके अलावा, लाइसेंस सर्वर से लाइसेंस पाने के लिए, आपको Media3 Exoplayer को सही DRM जानकारी के साथ कॉन्फ़िगर करना होगा.

यहां दिए गए उदाहरण में, DRM से सुरक्षित वीडियो स्ट्रीम चलाने के लिए, SpatialExternalSurface और ExoPlayer को कॉन्फ़िगर करने का तरीका बताया गया है:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun DrmSpatialVideoPlayer() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp)
                .height(676.dp),
            stereoMode = StereoMode.SideBySide,
            surfaceProtection = SurfaceProtection.Protected
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }

            // Define the URI for your DRM-protected content and license server.
            val videoUri = "https://your-content-provider.com/video.mpd"
            val drmLicenseUrl = "https://your-license-server.com/license"

            // Build a MediaItem with the necessary DRM configuration.
            val mediaItem = MediaItem.Builder()
                .setUri(videoUri)
                .setDrmConfiguration(
                    MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
                        .setLicenseUri(drmLicenseUrl)
                        .build()
                )
                .build()

            onSurfaceCreated { surface ->
                // The created surface is secure and can be used by the player.
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }

            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

कोड के बारे में अहम बातें

  • सुरक्षित सर्फ़ेस: surfaceProtection = SurfaceProtection.Protected को SpatialExternalSurface पर सेट करना ज़रूरी है, ताकि बुनियादी Surface को DRM कॉन्टेंट के लिए सुरक्षित बफ़र से बैकअप किया जा सके.
  • DRM कॉन्फ़िगरेशन: आपको MediaItem को DRM स्कीम (उदाहरण के लिए, C.WIDEVINE_UUID) और अपने लाइसेंस सर्वर के यूआरआई के साथ कॉन्फ़िगर करना होगा. ExoPlayer, DRM सेशन को मैनेज करने के लिए इस जानकारी का इस्तेमाल करता है.
  • सुरक्षित कॉन्टेंट: सुरक्षित सर्फ़ेस पर रेंडर करते समय, वीडियो कॉन्टेंट को डिकोड किया जाता है और सुरक्षित पाथ पर दिखाया जाता है. इससे, कॉन्टेंट लाइसेंसिंग की ज़रूरी शर्तें पूरी करने में मदद मिलती है. इससे, कॉन्टेंट को स्क्रीन कैप्चर में दिखने से भी रोका जा सकता है.

अन्य स्पेशल यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट जोड़ना

स्पेशल यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट को, आपके ऐप्लिकेशन की यूज़र इंटरफ़ेस (यूआई) हैरारकी में कहीं भी रखा जा सकता है. इन एलिमेंट को आपके 2D यूज़र इंटरफ़ेस (यूआई) में फिर से इस्तेमाल किया जा सकता है. साथ ही, इनके स्पेशल एट्रिब्यूट सिर्फ़ तब दिखेंगे, जब स्पेशल क्षमताओं को चालू किया जाएगा. इससे, मेन्यू, डायलॉग, और अन्य कॉम्पोनेंट में एलिवेशन जोड़ा जा सकता है. इसके लिए, आपको अपना कोड दो बार लिखने की ज़रूरत नहीं होती. इन एलिमेंट का इस्तेमाल करने का तरीका समझने के लिए, स्पेशल यूज़र इंटरफ़ेस (यूआई) के ये उदाहरण देखें.

यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट

स्पेशलाइज़ेशन की सुविधा चालू होने पर

2D एनवायरमेंट में

SpatialDialog

एलिवेटेड डायलॉग दिखाने के लिए, पैनल को z-डेप्थ में थोड़ा पीछे पुश किया जाएगा

2D Dialog पर वापस आ जाता है.

SpatialPopup

एलिवेटेड पॉप-अप दिखाने के लिए, पैनल को z-डेप्थ में थोड़ा पीछे पुश किया जाएगा

2D Popup पर वापस आ जाता है.

SpatialElevation

एलिवेशन जोड़ने के लिए, SpatialElevationLevel सेट किया जा सकता है.

स्पेशल एलिवेशन के बिना दिखता है.

SpatialDialog

यह एक ऐसे डायलॉग का उदाहरण है जो कुछ समय बाद खुलता है. When SpatialDialog का इस्तेमाल करने पर, डायलॉग उसी z-डेप्थ पर दिखता है जिस पर स्पेशल पैनल दिखता है. साथ ही, स्पेशलाइज़ेशन की सुविधा चालू होने पर, पैनल को 125dp पीछे पुश किया जाता है. SpatialDialog का इस्तेमाल तब भी किया जा सकता है, जब स्पेशलाइज़ेशन की सुविधा चालू न हो. इस मामले में, SpatialDialog, अपने 2D वर्शन Dialog पर वापस आ जाता है.

@Composable
fun DelayedDialog() {
    var showDialog by remember { mutableStateOf(false) }
    LaunchedEffect(Unit) {
        delay(3000)
        showDialog = true
    }
    if (showDialog) {
        SpatialDialog(
            onDismissRequest = { showDialog = false },
            SpatialDialogProperties(
                dismissOnBackPress = true
            )
        ) {
            Box(
                Modifier
                    .height(150.dp)
                    .width(150.dp)
            ) {
                Button(onClick = { showDialog = false }) {
                    Text("OK")
                }
            }
        }
    }
}

कोड के बारे में अहम बातें

कस्टम पैनल और लेआउट बनाना

XR के लिए Compose के साथ काम न करने वाले कस्टम पैनल बनाने के लिए, सीधे PanelEntity इंस्टेंस और सीन ग्राफ़ के साथ काम किया जा सकता है. इसके लिए, SceneCore एपीआई का इस्तेमाल करें.

ऑर्बिटर को स्पेशल पैनल और लेआउट से ऐंकर करना

Compose में डिक्लेयर किए गए SpatialPanels और स्पेशल लेआउट कॉम्पोनेंट से, ऑर्बिटर को ऐंकर किया जा सकता है. इसके लिए, यूज़र इंटरफ़ेस (यूआई) एलिमेंट के स्पेशल लेआउट में ऑर्बिटर को डिक्लेयर करना होता है. जैसे, SpatialRow, SpatialColumn या SpatialBox. ऑर्बिटर, उस पैरंट से ऐंकर होता है जो उस जगह के सबसे करीब होता है जहां आपने इसे डिक्लेयर किया है.

ऑर्बिटर का व्यवहार, इस बात पर निर्भर करता है कि आपने इसे कहां डिक्लेयर किया है:

  • 2D लेआउट में, जिसे SpatialPanel में रैप किया गया है (जैसा कि पहले दिए गए कोड स्निपेट में दिखाया गया है), ऑर्बिटर उस SpatialPanel से ऐंकर होता है.
  • Subspace में, ऑर्बिटर सबसे करीबी पैरंट एंटिटी से ऐंकर होता है. यह एंटिटी, वह स्पेशल लेआउट होता है जिसमें ऑर्बिटर को डिक्लेयर किया गया है.

यहां दिए गए उदाहरण में, ऑर्बिटर को स्पेशल रो से ऐंकर करने का तरीका बताया गया है:

Subspace {
    SpatialRow {
        Orbiter(
            position = ContentEdge.Top,
            offset = 8.dp,
            offsetType = OrbiterOffsetType.InnerEdge,
            shape = SpatialRoundedCornerShape(size = CornerSize(50))
        ) {
            Text(
                "Hello World!",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier
                    .background(Color.White)
                    .padding(16.dp)
            )
        }
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
        ) {
            Box(
                modifier = Modifier
                    .background(Color.Red)
            )
        }
        SpatialPanel(
            SubspaceModifier
                .height(824.dp)
                .width(1400.dp)
        ) {
            Box(
                modifier = Modifier
                    .background(Color.Blue)
            )
        }
    }
}

कोड के बारे में अहम बातें

  • जब किसी ऑर्बिटर को 2D लेआउट के बाहर डिक्लेयर किया जाता है, तो ऑर्बिटर अपने सबसे करीबी पैरंट एंटिटी से ऐंकर होता है. इस मामले में, ऑर्बिटर उस SpatialRow के सबसे ऊपर ऐंकर होता है जिसमें इसे डिक्लेयर किया गया है.
  • SpatialRow, SpatialColumn, SpatialBox जैसे स्पेशल लेआउट से, कॉन्टेंटलेस एंटिटी जुड़ी होती हैं. इसलिए, स्पेशल लेआउट में डिक्लेयर किया गया ऑर्बिटर, उस लेआउट से ऐंकर होता है.

इन्हें भी देखें