डिसप्ले सपोर्ट

डिसप्ले से जुड़े इन खास इलाकों में किए गए अपडेट नीचे दिए गए हैं:

गतिविधियों और डिसप्ले का साइज़ बदलें

किसी ऐप्लिकेशन में मल्टी-विंडो मोड या साइज़ बदलने की सुविधा काम नहीं करती, यह बताने के लिए resizeableActivity=false एट्रिब्यूट का इस्तेमाल किया जाता है. गतिविधियों का साइज़ बदलने पर, ऐप्लिकेशन को ये सामान्य समस्याएं आ सकती हैं:

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

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

जिन ऐप्लिकेशन ने मेनिफ़ेस्ट में इस एट्रिब्यूट को खास तौर पर false पर सेट किया है उन्हें मल्टी-विंडो मोड में लॉन्च नहीं किया जाना चाहिए. ऐसा तब तक नहीं किया जाना चाहिए, जब तक 'कंपैटबिलिटी मोड' लागू न हो:

  • प्रोसेस पर वही कॉन्फ़िगरेशन लागू होता है जिसमें सभी गतिविधियां और गैर-गतिविधि कॉम्पोनेंट शामिल होते हैं.
  • लागू किया गया कॉन्फ़िगरेशन, ऐप्लिकेशन के साथ काम करने वाले डिसप्ले के लिए सीडीडी की ज़रूरी शर्तों को पूरा करता हो.

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

डिफ़ॉल्ट रूप से लागू होने वाली नीति के तहत, यह सुविधा इन मामलों में काम नहीं करती:

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

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

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

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

जब resizeableActivity को सेट नहीं किया जाता है या इसे true पर सेट किया जाता है, तो ऐप्लिकेशन में साइज़ बदलने की सुविधा पूरी तरह से काम करती है.

लागू करना

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

अगर SCM गतिविधि पूरी स्क्रीन को भर नहीं सकती, तो उसे सबसे ऊपर अलाइन किया जाता है और फिर उसे हॉरिज़ॉन्टल तौर पर बीच में रखा जाता है. गतिविधि की सीमाओं का हिसाब, AppWindowToken#calculateCompatBoundsTransformation() ने लगाया है.

जब किसी एससीएम गतिविधि में, कंटेनर से अलग स्क्रीन कॉन्फ़िगरेशन का इस्तेमाल किया जाता है (उदाहरण के लिए, डिसप्ले का साइज़ बदलना या गतिविधि को किसी दूसरे डिसप्ले में ले जाना), तो ActivityRecord#inSizeCompatMode() सही होता है और SizeCompatModeActivityController (सिस्टम यूज़र इंटरफ़ेस में) को प्रोसेस को रीस्टार्ट करने का बटन दिखाने के लिए कॉलबैक मिलता है.

डिसप्ले साइज़ और आसपेक्ट रेशियो

Android 10 में नए आसपेक्ट रेशियो का इस्तेमाल किया जा सकता है. जैसे, लंबी और पतली स्क्रीन के आसपेक्ट रेशियो से लेकर 1:1 आसपेक्ट रेशियो तक. ऐप्लिकेशन, स्क्रीन के उस ApplicationInfo#maxAspectRatio और ApplicationInfo#minAspectRatio को तय कर सकते हैं जिसे वे मैनेज कर सकते हैं.

Android 10 में ऐप्लिकेशन के अनुपात

पहला डायग्राम. Android 10 में काम करने वाले ऐप्लिकेशन के अनुपात का उदाहरण

डिवाइस पर लागू किए जाने वाले दूसरे डिसप्ले में, Android 9 के लिए ज़रूरी साइज़ और रिज़ॉल्यूशन से कम और इससे कम (कम से कम 2.5 इंच चौड़ाई या ऊंचाई, smallestScreenWidth के लिए कम से कम 320 डीपी) हो सकते हैं. हालांकि, इन छोटे डिसप्ले के साथ काम करने के लिए ऑप्ट-इन करने वाली गतिविधियां ही यहां डाली जा सकती हैं.

ऐप्लिकेशन, टारगेट किए गए डिसप्ले साइज़ से कम या उसके बराबर का कम से कम साइज़ तय करके ऑप्ट-इन कर सकते हैं. ऐसा करने के लिए, AndroidManifest में android:minHeight और android:minWidth गतिविधि लेआउट एट्रिब्यूट का इस्तेमाल करें.

डिसप्ले से जुड़ी नीतियां

Android 10, कुछ डिसप्ले नीतियों को PhoneWindowManager में लागू होने वाले डिफ़ॉल्ट WindowManagerPolicy से अलग करके, हर डिसप्ले क्लास में ले जाता है, जैसे कि:

  • डिसप्ले की स्थिति और रोटेशन
  • कुछ बटन और मोशन इवेंट ट्रैकिंग
  • सिस्टम यूज़र इंटरफ़ेस (यूआई) और सजावट वाली विंडो

Android 9 (और उससे पहले के वर्शन) में, PhoneWindowManager क्लास में डिसप्ले नीतियां, स्थिति और सेटिंग, रोटेशन, विंडो फ़्रेम की सजावट की ट्रैकिंग वगैरह मैनेज की जाती थी. Android 10, रोटेशन ट्रैकिंग को छोड़कर, इसके ज़्यादातर हिस्से को DisplayPolicy क्लास में ले जाता है. हालांकि, रोटेशन ट्रैकिंग को DisplayRotation में ट्रांसफ़र कर दिया गया है.

डिसप्ले विंडो की सेटिंग

Android 10 में, हर डिसप्ले के लिए कॉन्फ़िगर की जा सकने वाली विंडो की सेटिंग को बड़ा किया गया है. इसमें ये चीज़ें शामिल हैं:

  • डिफ़ॉल्ट डिसप्ले विंडो मोड
  • ओवरस्कैन वैल्यू
  • उपयोगकर्ता की स्क्रीन का रोटेशन और रोटेशन मोड
  • फ़ोर्स्ड साइज़, डेंसिटी, और स्केलिंग मोड
  • कॉन्टेंट हटाने वाला मोड (डिसप्ले हटाए जाने पर)
  • सिस्टम की सजावट और IME के लिए सहायता

DisplayWindowSettings क्लास में इन विकल्पों के लिए सेटिंग शामिल होती हैं. जब भी कोई सेटिंग बदली जाती है, तो ये डिस्क में /data सेक्शन में सेव हो जाती हैं. ज़्यादा जानकारी के लिए, DisplayWindowSettings.AtomicFileStorage और DisplayWindowSettings#writeSettings() देखें. डिवाइस बनाने वाली कंपनियां, अपने डिवाइस कॉन्फ़िगरेशन के लिए display_settings.xml में डिफ़ॉल्ट वैल्यू दे सकती हैं. हालांकि, फ़ाइल को /data में सेव किया गया है. इसलिए, अगर फ़ाइल को वाइप करके मिटा दिया जाता है, तो उसे वापस लाने के लिए अलग तरीके की ज़रूरत हो सकती है.

डिफ़ॉल्ट रूप से, सेटिंग को बनाए रखते समय Android 10, डिसप्ले के लिए आइडेंटिफ़ायर के तौर पर DisplayInfo#uniqueId का इस्तेमाल करता है. uniqueId को सभी डिसप्ले के लिए पॉप्युलेट किया जाना चाहिए. इसके अलावा, यह फ़िज़िकल और नेटवर्क डिसप्ले के लिए भी काम करता है. आइडेंटिफ़ायर के तौर पर, किसी फ़िज़िकल डिसप्ले के पोर्ट का इस्तेमाल भी किया जा सकता है. इसे DisplayWindowSettings#mIdentifier में सेट किया जा सकता है. हर बार डेटा सेव करने पर, सभी सेटिंग सेव हो जाती हैं. इससे, स्टोरेज में डिसप्ले एंट्री के लिए इस्तेमाल की जाने वाली पासकोड को अपडेट करना सुरक्षित हो जाता है. ज़्यादा जानकारी के लिए, स्टैटिक डिसप्ले आइडेंटिफ़ायर देखें.

सेटिंग को /data डायरेक्ट्री में इसलिए सेव किया जाता है, ताकि पुरानी सेटिंग को ऐक्सेस किया जा सके. पहले, इनका इस्तेमाल डिसप्ले रोटेशन जैसी उपयोगकर्ता की सेट की गई सेटिंग को बनाए रखने के लिए किया जाता था.

स्टैटिक डिसप्ले आइडेंटिफ़ायर

Android 9 (और उससे पहले के वर्शन) ने फ़्रेमवर्क में डिसप्ले के लिए स्थायी आइडेंटिफ़ायर उपलब्ध नहीं कराए हैं. जब सिस्टम में डिसप्ले जोड़ा गया था, तो एक स्टैटिक काउंटर बढ़ाकर, उस डिसप्ले के लिए Display#mDisplayId या DisplayInfo#displayId जनरेट किया गया था. अगर सिस्टम ने उसी डिसप्ले को जोड़ा और हटाया था, तो एक अलग आईडी मिला.

अगर किसी डिवाइस में बूट होने के बाद एक से ज़्यादा डिसप्ले उपलब्ध थे, तो समय के हिसाब से डिसप्ले को अलग-अलग आइडेंटिफ़ायर असाइन किए जा सकते थे. Android 9 (और इससे पहले के वर्शन) में DisplayInfo#uniqueId शामिल था. हालांकि, इसमें डिसप्ले के बीच अंतर करने के लिए ज़रूरत के मुताबिक जानकारी नहीं थी. ऐसा इसलिए था, क्योंकि डिवाइस में पहले से मौजूद और बाहरी डिसप्ले को दिखाने के लिए, फ़िज़िकल डिसप्ले को local:0 या local:1 के तौर पर पहचाना जाता था.

Android 10, स्थायी आइडेंटिफ़ायर जोड़ने के साथ-साथ लोकल, नेटवर्क, और वर्चुअल डिसप्ले के बीच अंतर करने के लिए, DisplayInfo#uniqueId में बदलाव करता है.

डिसप्ले टाइप फ़ॉर्मैट
लोकल
local:<stable-id>
नेटवर्क
network:<mac-address>
वर्चुअल
virtual:<package-name-and-name>

uniqueId में किए गए अपडेट के अलावा, DisplayInfo.address में DisplayAddress भी शामिल है. यह एक ऐसा डिसप्ले आइडेंटिफ़ायर है जो रीबूट के दौरान भी स्थिर रहता है. Android 10 में, DisplayAddress फ़िज़िकल और नेटवर्क डिसप्ले के साथ काम करता है. DisplayAddress.Physical में एक स्थायी डिसप्ले आईडी (uniqueId के जैसा) होता है और इसे DisplayAddress#fromPhysicalDisplayId() की मदद से बनाया जा सकता है.

Android 10 में, पोर्ट की जानकारी (Physical#getPort()) पाने का एक आसान तरीका भी दिया गया है. इस तरीके का इस्तेमाल, फ़्रेमवर्क में डिसप्ले की स्टैटिक पहचान करने के लिए किया जा सकता है. उदाहरण के लिए, इसका इस्तेमाल DisplayWindowSettings में किया जाता है. DisplayAddress.Network में MAC पता होता है और इसे DisplayAddress#fromMacAddress() की मदद से बनाया जा सकता है.

इन बदलावों की मदद से, डिवाइस बनाने वाली कंपनियां स्टैटिक मल्टी-डिसप्ले सेट-अप में डिसप्ले की पहचान कर सकती हैं. साथ ही, स्टैटिक डिसप्ले आइडेंटिफ़ायर का इस्तेमाल करके, सिस्टम की अलग-अलग सेटिंग और सुविधाओं को कॉन्फ़िगर कर सकती हैं. जैसे, फ़िज़िकल डिसप्ले के लिए पोर्ट. ये तरीके छिपा दिए गए हैं. इन्हें सिर्फ़ system_server में इस्तेमाल किया जाना चाहिए.

एचडब्ल्यूसी डिसप्ले आईडी दिया जाता है, जो साफ़ नहीं होता और हमेशा स्थिर नहीं होता. यह तरीका, (प्लैटफ़ॉर्म के हिसाब से) 8-बिट वाला 8-बिट पोर्ट नंबर दिखाता है, जो डिसप्ले आउटपुट के लिए फ़िज़िकल कनेक्टर के साथ-साथ डिसप्ले के ईडीआईडी ब्लॉब की पहचान करता है. SurfaceFlinger, ईडीआईडी से मैन्युफ़ैक्चरर या मॉडल की जानकारी निकालता है, ताकि हम फ़्रेमवर्क के मुताबिक काम करने वाले 64-बिट वाले डिसप्ले आईडी जनरेट कर सकें. अगर यह तरीका काम नहीं करता है या गड़बड़ियां ठीक हो जाती हैं, तो SurfaceFlinger वापस लेगसी एमडी मोड पर चला जाता है. यहां DisplayInfo#address शून्य है और DisplayInfo#uniqueId हार्ड कोड किया गया है, जैसा कि ऊपर बताया गया है.

यह सुविधा काम करती है या नहीं, इसकी पुष्टि करने के लिए इसे चलाएं:

$ dumpsys SurfaceFlinger --display-id
# Example output.
Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"

दो से ज़्यादा डिसप्ले का इस्तेमाल करना

Android 9 और इससे पहले के वर्शन में, SurfaceFlinger और DisplayManagerService में हार्ड कोड किए गए आईडी 0 और 1 वाले ज़्यादा से ज़्यादा दो फ़िज़िकल डिसप्ले होने चाहिए.

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

फ़्रेमवर्क, SurfaceControl#getPhysicalDisplayIds या DisplayEventReceiver hotplug इवेंट से 64-बिट डिसप्ले आईडी पाने के बाद, SurfaceControl#getPhysicalDisplayToken की मदद से किसी फ़िज़िकल डिसप्ले के लिए IBinder टोकन देख सकता है.

Android 10 और उससे पहले के वर्शन में, मुख्य इंटरनल डिसप्ले TYPE_INTERNAL है. साथ ही, सभी सेकंडरी डिसप्ले को TYPE_EXTERNAL के तौर पर फ़्लैग किया गया है, भले ही कनेक्शन किसी भी तरह का हो. इसलिए, अतिरिक्त इंटरनल डिसप्ले को बाहरी डिसप्ले माना जाता है. इस समस्या को हल करने के लिए, डिवाइस के हिसाब से कोड, DisplayAddress.Physical#getPort के बारे में अनुमान लगा सकता है. ऐसा तब किया जा सकता है, जब एचडब्ल्यूसी के बारे में पता हो और पोर्ट के बंटवारे के लॉजिक का अनुमान लगाया जा सकता हो.

इस पाबंदी को Android 11 और उसके बाद के वर्शन में हटा दिया गया है.

  • Android 11 में, बूट के दौरान रिपोर्ट किया गया पहला डिसप्ले, प्राइमरी डिसप्ले होता है. कनेक्शन का टाइप (इंटरनल बनाम एक्सटर्नल) मायने नहीं रखता. हालांकि, यह सच है कि प्राइमरी डिसप्ले को डिसकनेक्ट नहीं किया जा सकता. साथ ही, यह भी ज़रूरी है कि वह डिसप्ले, डिवाइस में पहले से मौजूद हो. ध्यान दें कि फ़ोल्ड किए जा सकने वाले कुछ फ़ोन में, एक से ज़्यादा डिसप्ले होते हैं.
  • कनेक्शन के टाइप के आधार पर, सेकंडरी डिसप्ले को Display.TYPE_INTERNAL या Display.TYPE_EXTERNAL (पहले इन्हें Display.TYPE_BUILT_IN और Display.TYPE_HDMI कहा जाता था) की कैटगरी में सही कैटगरी में रखा जाता है.

लागू करना

Android 9 और उससे पहले के वर्शन में, डिसप्ले की पहचान 32-बिट आईडी से की जाती है. इनमें 0 का मतलब इंटरनल डिसप्ले है, 1 है एक्सटर्नल डिसप्ले, [2, INT32_MAX] एचडब्ल्यूसी वर्चुअल डिसप्ले हैं, और -1, अमान्य डिसप्ले या नॉन-एचडब्ल्यूसी वर्चुअल डिसप्ले को दिखाता है.

Android 10 से, डिसप्ले को स्थिर और लगातार काम करने वाले आईडी दिए जाते हैं. इससे SurfaceFlinger और DisplayManagerService, दो से ज़्यादा डिसप्ले को ट्रैक कर सकते हैं और पहले देखे गए डिसप्ले को पहचान सकते हैं. अगर एचडब्ल्यूसी IComposerClient.getDisplayIdentificationData के साथ काम करता है और डिसप्ले की पहचान से जुड़ा डेटा देता है, तो SurfaceFlinger ईडीआईडी स्ट्रक्चर को पार्स करता है. साथ ही, फ़िज़िकल और एचडब्ल्यूसी वर्चुअल डिसप्ले के लिए स्टेबल 64-बिट डिसप्ले आईडी असाइन करता है. आईडी को किसी विकल्प के टाइप का इस्तेमाल करके दिखाया जाता है. यहां शून्य वैल्यू, अमान्य डिसप्ले या नॉन-एचडब्ल्यूसी वर्चुअल डिसप्ले को दिखाती है. HW के साथ काम न करने पर, SurfaceFlinger में ज़्यादा से ज़्यादा दो फ़िज़िकल डिसप्ले आते हैं.

हर डिसप्ले के हिसाब से फ़ोकस

एक ही समय पर अलग-अलग डिसप्ले को टारगेट करने वाले कई इनपुट सोर्स के साथ काम करने के लिए, Android 10 को कॉन्फ़िगर किया जा सकता है. इससे, एक डिसप्ले पर ज़्यादा से ज़्यादा एक फ़ोकस वाली विंडो काम कर सकती है. यह सिर्फ़ खास तरह के डिवाइसों के लिए है. ऐसा तब किया जाता है, जब कई उपयोगकर्ता एक ही समय पर एक ही डिवाइस से इंटरैक्ट करते हैं और वे अलग-अलग इनपुट के तरीकों या डिवाइसों का इस्तेमाल करते हैं. जैसे, Android Automotive.

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

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

हालांकि, छिपे हुए वर्चुअल डिसप्ले का इस्तेमाल करके कीबोर्ड (हार्डवेयर या सॉफ़्टवेयर) से मिले इनपुट को सबसे ज़्यादा

हर डिसप्ले पर फ़ोकस सेट करने के लिए com.android.internal.R.bool.config_perDisplayFocusEnabled का इस्तेमाल करें.

इनके साथ काम करता है

समस्या: Android 9 और उससे पहले वाले वर्शन में, सिस्टम में एक बार में ज़्यादा से ज़्यादा एक विंडो फ़ोकस में रह सकती है.

समाधान: बहुत कम मामलों में जब एक ही प्रोसेस से दो विंडो पर फ़ोकस किया जाता है, तो सिस्टम सिर्फ़ उस विंडो पर फ़ोकस करता है जो Z-ऑर्डर में ऊपर होती है. यह पाबंदी उन ऐप्लिकेशन से हटा दी जाती है जो Android 10 को टारगेट करते हैं. ऐसा होने पर, एक समय पर कई विंडो पर फ़ोकस किया जा सकता है.

लागू करना

WindowManagerService#mPerDisplayFocusEnabled से इस सुविधा की उपलब्धता को कंट्रोल किया जाता है. ActivityManager में, अब वैरिएबल में ग्लोबल ट्रैकिंग के बजाय ActivityDisplay#getFocusedStack() का इस्तेमाल किया जाता है. ActivityDisplay#getFocusedStack() यह वैल्यू को कैश मेमोरी में सेव करने के बजाय, Z-ऑर्डर के आधार पर फ़ोकस करता है. ऐसा इसलिए है, ताकि सिर्फ़ एक स्रोत, WindowManager को गतिविधियों के Z-क्रम को ट्रैक करने की ज़रूरत पड़े.

ActivityStackSupervisor#getTopDisplayFocusedStack() भी उन मामलों में यही तरीका अपनाता है, जब सिस्टम में सबसे ज़्यादा फ़ोकस किए गए स्टैक की पहचान की जानी चाहिए. स्टैक को ऊपर से नीचे तक ट्रैवर्स किया जाता है, ताकि ज़रूरी शर्तें पूरी करने वाला पहला स्टैक ढूंढा जा सके.

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

InputDispatcher::mFocusedWindowHandlesByDisplay और InputDispatcher::setFocusedDisplay() देखें. फ़ोकस किए गए ऐप्लिकेशन, NativeInputManager::setFocusedApplication() के ज़रिए InputManagerService में भी अलग से अपडेट किए जाते हैं.

WindowManager में, फ़ोकस की गई विंडो को भी अलग से ट्रैक किया जाता है. DisplayContent#mCurrentFocus और DisplayContent#mFocusedApp के साथ-साथ इनसे जुड़े इस्तेमाल देखें. संबंधित फ़ोकस को ट्रैक करने और अपडेट करने के तरीकों को WindowManagerService से बदलकर DisplayContent कर दिया गया है.