डेस्कटॉप विंडोिंग का समर्थन

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

पहली इमेज में, डेस्कटॉप विंडोविंग की सुविधा चालू होने पर, स्क्रीन का संगठन दिखाया गया है. इन बातों का ध्यान रखें:

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

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

जब कोई ऐप्लिकेशन डेस्कटॉप विंडोविंग में खुलता है, तो अन्य ऐप्लिकेशन भी डेस्कटॉप विंडो में खुलते हैं.

दूसरी इमेज. डेस्कटॉप विंडोविंग में जाने के लिए, ऐप्लिकेशन विंडो के हैंडल को दबाकर रखें और खींचें.

उपयोगकर्ता, डेस्कटॉप विंडोविंग को उस मेन्यू से भी चालू कर सकते हैं जो विंडो हैंडल के नीचे दिखता है. यह मेन्यू, हैंडल पर टैप या क्लिक करने पर दिखता है. इसके अलावा, कीबोर्ड शॉर्टकट Meta key (Windows, Command या Search) + Ctrl + Down का इस्तेमाल करके भी, डेस्कटॉप विंडोविंग को चालू किया जा सकता है.

उपयोगकर्ता, डेस्कटॉप विंडोविंग से बाहर निकलने के लिए, सभी चालू विंडो बंद कर सकते हैं. इसके अलावा, डेस्कटॉप विंडो के सबसे ऊपर मौजूद विंडो हैंडल को पकड़कर, ऐप्लिकेशन को स्क्रीन के सबसे ऊपर खींचकर भी, डेस्कटॉप विंडोविंग से बाहर निकला जा सकता है. कीबोर्ड शॉर्टकट Meta + H का इस्तेमाल करके भी, डेस्कटॉप विंडोविंग से बाहर निकला जा सकता है. इससे ऐप्लिकेशन फिर से फ़ुल स्क्रीन मोड में चलने लगते हैं.

डेस्कटॉप विंडोविंग पर वापस जाने के लिए, हाल ही में इस्तेमाल किए गए ऐप्लिकेशन वाली स्क्रीन पर, डेस्कटॉप स्पेस टाइल पर टैप या क्लिक करें.

डेस्कटॉप जैसे एनवायरमेंट के लिए, अपने ऐप्लिकेशन के लेआउट को ऑप्टिमाइज़ करना

डेस्कटॉप के लिए डिज़ाइन करना, मोबाइल के लिए डिज़ाइन करने से अलग हो सकता है. इसकी वजह है, स्क्रीन का बड़ा होना, माउस और कीबोर्ड से सटीक इनपुट मिलना, और ज़्यादा प्रॉडक्टिविटी की उम्मीद होना.

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

lifecycleScope.launch(Dispatchers.Main) {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        windowInfoTracker.windowEngagementInfo(this@DesktopWindowingActivity)
            .collect { windowEngagementInfo ->
                if(windowEngagementInfo.hasEngagementMode(WindowEngagementInfo.EngagementMode.PRECISE_POINTER)){
                    showDesktopOptimizedUI()
                }else {
                    showTouchOptimizedUI()
                }
        }
    }
}

ज़्यादा जानने के लिए, डेस्कटॉप के लिए डिज़ाइन करना लेख पढ़ें.

विंडो का साइज़ बदलने की सुविधा और कंपैटबिलिटी मोड

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

तीसरी इमेज. पोर्ट्रेट ओरिएंटेशन में लॉक किए गए ऐप्लिकेशन की विंडो का साइज़ बदलकर उसे लैंडस्केप ओरिएंटेशन में देखना.

जिन ऐप्लिकेशन को साइज़ बदलने की अनुमति नहीं होती (यानी, resizeableActivity = false), उनके यूज़र इंटरफ़ेस (यूआई) को उसी आसपेक्ट रेशियो (चौड़ाई-ऊंचाई का अनुपात) में स्केल किया जाता है.

चौथी इमेज. जिन ऐप्लिकेशन को साइज़ बदलने की अनुमति नहीं होती उनकी विंडो का साइज़ बदलने पर, उनके यूज़र इंटरफ़ेस (यूआई) को स्केल किया जाता है.

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

जब तक ऐप्लिकेशन, पूरी तरह से रिस्पॉन्सिव कैमरा व्यूफ़ाइंडर लागू नहीं कर लेते, तब तक खास ट्रीटमेंट की मदद से, उपयोगकर्ता को बेहतर अनुभव मिलता है. इससे गलत मान्यताओं की वजह से होने वाले असर को कम किया जा सकता है.

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

पांचवी इमेज. विंडो का साइज़ बदलने पर भी, कैमरा व्यूफ़ाइंडर का आसपेक्ट रेशियो (चौड़ाई-ऊंचाई का अनुपात) वही रहता है.

पसंद के मुताबिक बनाए जा सकने वाले हेडर इनसेट

डेस्कटॉप विंडोविंग में चलने वाले सभी ऐप्लिकेशन में एक हेडर बार होता है. इमर्सिव मोड में भी ऐसा होता है. इस बार को पसंद के मुताबिक बनाया जा सकता है, ताकि आपके ऐप्लिकेशन का कॉन्टेंट न दिखे और कस्टम यूज़र इंटरफ़ेस (यूआई) एलिमेंट को सीधे हेडर स्पेस में बनाया जा सके.

कस्टम हेडर लागू करने से पहले और बाद में Chrome.
छठी इमेज. कस्टम हेडर लागू करने से पहले और बाद में Chrome.

लागू करना

हेडर बार में कस्टम कॉन्टेंट बनाने के लिए, पहला चरण है कि हेडर बार के बैकग्राउंड को पारदर्शी बनाया जाए. WindowInsetsController के साथ APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND फ़्लैग का इस्तेमाल करके, ऐसा किया जा सकता है.

window.insetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
)

हेडर बार के पारदर्शी होने के बाद, हेडर वाले हिस्से को अपने ऐप्लिकेशन के डिज़ाइन के हिसाब से स्टाइल किया जा सकता है. WindowInsets.isCaptionBarVisible का इस्तेमाल करके, यह पता लगाया जा सकता है कि बार मौजूद है या नहीं. इसके बाद, अपने लेआउट पर सही ऊंचाई या पैडिंग लागू की जा सकती है.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CaptionBar() {
    if (WindowInsets.isCaptionBarVisible) {
        Row(
            modifier = Modifier
                .windowInsetsTopHeight(WindowInsets.captionBar)
                .fillMaxWidth()
                .background(if (isSystemInDarkTheme()) Color.White else Color.Black),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Caption Bar Title",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(4.dp)
            )
        }
    }
}

  • setSystemBarsAppearance(appearance,mask): सिस्टम बार के विज़ुअल स्टाइल को कॉन्फ़िगर करता है. पहला पैरामीटर, टारगेट अपीयरेंस फ़्लैग तय करता है. वहीं, दूसरा पैरामीटर, मास्क के तौर पर काम करता है, ताकि यह कंट्रोल किया जा सके कि किन खास फ़्लैग में बदलाव किया गया है.

  • windowInsetsTopHeight(): आपके Composable की ऊंचाई को सिस्टम के हेडर बार के हिसाब से अपने-आप सेट करता है. इससे, पिक्सल वैल्यू को हार्डकोड किए बिना, आपका कस्टम बैकग्राउंड कैप्शन वाले हिस्से में फ़िट हो जाता है.

  • WindowInsets.captionBar: डेस्कटॉप विंडोविंग कंट्रोल (बंद करें, मैक्सिमाइज़ करें वगैरह) के डाइमेंशन उपलब्ध कराता है. इससे, डेस्कटॉप विंडोविंग में जाने या उससे बाहर निकलने पर, आपका यूज़र इंटरफ़ेस (यूआई) अपने-आप स्केल हो जाता है या छिप जाता है.

ज़्यादा जानकारी के लिए, विंडो इनसेट के बारे में लेख पढ़ें. टाइटल के अलावा, कैप्शन बार में अन्य यूज़र इंटरफ़ेस (यूआई) एलिमेंट भी दिखाए जा सकते हैं. जैसे, टैब—Google Chrome की तरह—खोज बार या प्रोफ़ाइल अवतार.

बेहतर यूज़र इंटरफ़ेस

Android 15 में, WindowInsets#getBoundingRects() तरीका उपलब्ध है. इसकी मदद से, सिस्टम बटन के साथ अपने यूज़र इंटरफ़ेस (यूआई) को ओवरलैप होने से बचाया जा सकता है. यह तरीका, Rect ऑब्जेक्ट की सूची दिखाता है. ये ऑब्जेक्ट, सिस्टम एलिमेंट से घिरे इलाकों को दिखाते हैं. कैप्शन बार में बची हुई जगह, सेफ़ ज़ोन होती है. इसमें, कस्टम कॉन्टेंट को सुरक्षित तरीके से रखा जा सकता है.

` APPEARANCE_LIGHT_CAPTION_BARS` का इस्तेमाल करके, हल्के और गहरे रंग वाली थीम के लिए, सिस्टम कैप्शन एलिमेंट के दिखने के तरीके को टॉगल करें. Compose में WindowInsets.Companion.captionBar() या Views में WindowInsets.Type.captionBar() का इस्तेमाल करके, इनसेट ऐक्सेस करें.

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

मल्टीटास्किंग और एक से ज़्यादा इंस्टेंस चलाने की सुविधा

डेस्कटॉप विंडोविंग में मल्टीटास्किंग की सुविधा मुख्य होती है. साथ ही, अपने ऐप्लिकेशन के कई इंस्टेंस चलाने की अनुमति देने से, उपयोगकर्ताओं की प्रॉडक्टिविटी बढ़ सकती है.

Android 15 से, PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI का इस्तेमाल किया जा सकता है. अपने AndroidManifest.xml में इस प्रॉपर्टी को सेट करके, यह तय किया जा सकता है कि सिस्टम यूज़र इंटरफ़ेस (यूआई) को ऐप्लिकेशन के कई इंस्टेंस लॉन्च करने के लिए विकल्प (जैसे, "नई विंडो" बटन) उपलब्ध कराने चाहिए.

<application>
    <property
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</application>

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

ड्रैग करने के जेस्चर की मदद से, ऐप्लिकेशन के इंस्टेंस मैनेज करना

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

सातवीं इमेज. डेस्कटॉप विंडो से टैब को ड्रैग करके, Chrome का नया इंस्टेंस शुरू करना.

ड्रैग-एंड-ड्रॉप की मदद से डेटा ट्रांसफ़र करना

किसी कंपोज़ेबल को एक से ज़्यादा इंस्टेंस ड्रैग-एंड-ड्रॉप के लिए ड्रैग सोर्स के तौर पर कॉन्फ़िगर करने के लिए, dragAndDropSource मॉडिफ़ायर का इस्तेमाल करें. इससे उपयोगकर्ता, आपके ऐप्लिकेशन के किसी दूसरे इंस्टेंस में कॉन्टेंट को ड्रैग कर सकते हैं या स्क्रीन के खाली हिस्से पर कॉन्टेंट को ड्रॉप करके, नया इंस्टेंस बना सकते हैं. इसके लैम्डा में, DragAndDropTransferData दिखाएं. साथ ही, ट्रांसफ़र करने के लिए डेटा वाले ClipData और एक से ज़्यादा इंस्टेंस के व्यवहार को कॉन्फ़िगर करने के लिए फ़्लैग पास करें.

Android 15 में, डेस्कटॉप-स्टाइल विंडोविंग और एक से ज़्यादा इंस्टेंस इंटरैक्शन के लिए दो मुख्य फ़्लैग जोड़े गए हैं:

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: इससे पता चलता है कि ड्रैग ऑपरेशन, विंडो की सीमाओं को पार कर सकता है. यह सुविधा, एक ही ऐप्लिकेशन के कई इंस्टेंस के लिए काम करती है. जब startDragAndDrop() को इस फ़्लैग को सेट करके कॉल किया जाता है, तब सिर्फ़ एक ही ऐप्लिकेशन की दिखने वाली विंडो, ड्रैग ऑपरेशन में हिस्सा ले सकती हैं और ड्रैग किया गया कॉन्टेंट पा सकती हैं.

Modifier.dragAndDropSource { _ ->
    DragAndDropTransferData(
        clipData = ClipData.newPlainText("label", "Your data"),
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION
    )
}

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: अगर कोई अन्य विंडो, ड्रॉप को हैंडल नहीं करती है, तो उपयोगकर्ता ड्रैग किए गए कॉन्टेंट को स्क्रीन के खाली हिस्से पर ड्रॉप करके, आपके ऐप्लिकेशन का नया इंस्टेंस शुरू कर सकते हैं.

Modifier.dragAndDropSource { _ ->
    val intent = Intent.makeMainActivity(activity.componentName).apply {
        putExtra("EXTRA_ITEM_ID", itemId)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
                Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
    }

    val pendingIntent = PendingIntent.getActivity(
        activity, 0, intent, PendingIntent.FLAG_IMMUTABLE
    )

    val data = ClipData(
        "Item $itemId",
        arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT),
        ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build()
    )

    DragAndDropTransferData(
        clipData = data,
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or
                View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
    )
}

ट्रांसफ़र किया गया डेटा पाना

किसी दूसरे इंस्टेंस से डेटा स्वीकार करने के लिए, dragAndDropTarget मॉडिफ़ायर का इस्तेमाल करें. अगर डेटा किसी दूसरे इंस्टेंस या ऐप्लिकेशन से आ रहा है, तो आपको साफ़ तौर पर अनुमतियों का अनुरोध करना होगा.

Modifier.dragAndDropTarget(
    shouldStartDragAndDrop = { event ->
        event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
    },
    target = object : DragAndDropTarget {
        override fun onDrop(event: DragAndDropEvent): Boolean {
            requestDragAndDropPermissions(activity, event.toAndroidDragEvent())
            val clipData = event.toAndroidDragEvent().clipData
            val item = clipData?.getItemAt(0)?.text
            if (item != null) {
                // Process the dropped text item here
            }
            return item != null
        }
    }
)

मुख्य चरण:

  • फ़िल्टर: यह देखने के लिए कि आने वाला डेटा (MIME टाइप) काम करता है या नहीं, shouldStartDragAndDrop का इस्तेमाल करें.
  • अनुमतियां: डेटा ऐक्सेस करने के लिए, requestDragAndDropPermissions(event) को कॉल करें.
  • हैंडल: onDrop कॉलबैक में डेटा एक्सट्रैक्ट करें.

अन्य ऑप्टिमाइज़ेशन

ऐप्लिकेशन लॉन्च करने की प्रोसेस को पसंद के मुताबिक बनाएं. साथ ही, ऐप्लिकेशन को डेस्कटॉप विंडोविंग से फ़ुल स्क्रीन मोड में ट्रांज़िशन करें.

डिफ़ॉल्ट साइज़ और पोज़िशन तय करना

यह ज़रूरी नहीं कि सभी ऐप्लिकेशन, भले ही उनका साइज़ बदला जा सके, उपयोगकर्ता को वैल्यू देने के लिए बड़ी विंडो का इस्तेमाल करें. You ActivityOptions#setLaunchBounds() तरीके का इस्तेमाल करके, गतिविधि लॉन्च होने पर डिफ़ॉल्ट साइज़ और पोज़िशन तय की जा सकती है.

डेस्कटॉप स्पेस से फ़ुल स्क्रीन मोड में जाना

ऐप्लिकेशन, Activity#requestFullScreenMode() को कॉल करके फ़ुल स्क्रीन मोड में जा सकते हैं. यह तरीका, ऐप्लिकेशन को डेस्कटॉप विंडोविंग से सीधे फ़ुल स्क्रीन मोड में दिखाता है.