إظهار الدعم

في ما يلي التعديلات التي تم إجراؤها على هذه المناطق الخاصة بالشاشة:

تغيير حجم الأنشطة والشاشات

للإشارة إلى أنّ التطبيق قد لا يتيح وضع "النوافذ المتعددة" أو تغيير الحجم، تستخدم الأنشطة السمة resizeableActivity=false. تشمل الصعوبات الشائعة التي تواجهها التطبيقات عند تغيير حجم الأنشطة ما يلي:

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

في Android 7 (والإصدارات الأحدث)، يمكن ضبط تطبيق resizeableActivity=false لتشغيله دائمًا في وضع ملء الشاشة. في هذه الحالة، تمنع المنصة الأنشطة التي لا يمكن تغيير حجمها من الانتقال إلى وضع الشاشة المُقسّمة. إذا حاول المستخدم تشغيل نشاط لا يمكن تغيير حجمه من مشغّل التطبيقات بينما كان في وضع تقسيم الشاشة، سيخرج النظام الأساسي من وضع تقسيم الشاشة ويشغّل النشاط الذي لا يمكن تغيير حجمه في وضع ملء الشاشة.

يجب عدم تشغيل التطبيقات التي تضبط هذه السمة على false في البيان بشكل صريح في وضع النوافذ المتعدّدة، ما لم يتم تطبيق وضع التوافق:

  • يتم تطبيق الإعدادات نفسها على العملية التي تحتوي على جميع الأنشطة ومكوّنات غير الأنشطة.
  • أن تستوفي الإعدادات المطبَّقة متطلبات CDD للشاشة المتوافقة مع التطبيق

في Android 10، لا تزال المنصة تمنع الأنشطة التي لا يمكن تغيير حجمها من الانتقال إلى وضع تقسيم الشاشة، ولكن يمكن تصغيرها مؤقتًا إذا حدّد النشاط اتجاهًا أو نسبة قياس ثابتة. وإذا لم يكن الأمر كذلك، يتم تغيير حجم النشاط لملء الشاشة بالكامل كما هو الحال في الإصدار 9 من Android والإصدارات الأقدم.

يطبّق التنفيذ التلقائي السياسة التالية:

عندما يكون النشاط غير متوافق مع وضع "النوافذ المتعددة" من خلال استخدام السمة android:resizeableActivity وعندما يستوفي هذا النشاط أحد الشروط الموضّحة أدناه، عند الحاجة إلى تغيير إعدادات الشاشة المطبَّقة، يتم حفظ النشاط والعملية باستخدام الإعدادات الأصلية ويتم تزويد المستخدم بخيار لإعادة تشغيل عملية التطبيق لاستخدام إعدادات الشاشة المعدَّلة.

  • يكون الاتجاه ثابتًا من خلال تطبيق android:screenOrientation
  • يحتوي التطبيق على نسبة عرض إلى ارتفاع قصوى أو دنيا تلقائية من خلال استهداف مستوى واجهة برمجة التطبيقات أو يعلن عن نسبة العرض إلى الارتفاع صراحةً

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

عند فتح الجهاز، لا تتغيّر الإعدادات والحجم ونسبة العرض إلى الارتفاع للنشاط، ولكن يظهر خيار إعادة تشغيل النشاط.

في حال عدم ضبط resizeableActivity (أو ضبطه على true)، يتيح التطبيق تغيير الحجم بالكامل.

التنفيذ

يُشار إلى النشاط الذي لا يمكن تغيير حجمه والذي يكون باتجاه أو نسبة عرض إلى ارتفاع ثابتة باسم وضع التوافق مع الحجم (SCM) في الرمز البرمجي. يتم تحديد الشرط في ActivityRecord#shouldUseSizeCompatMode(). عند بدء أحد أنشطة SCM، يتم تثبيت الإعدادات ذات الصلة بالشاشة (مثل الحجم أو الكثافة) في الإعدادات المطلوبة للتجاوز، وبالتالي لا يعتمد النشاط بعد ذلك على إعدادات العرض الحالية.

إذا تعذّر على نشاط SCM ملء الشاشة بالكامل، تتم محاذاته في أعلى الشاشة ويُوضع في وسطها أفقيًا. يتم احتساب حدود النشاط من قِبل AppWindowToken#calculateCompatBoundsTransformation().

عندما يستخدم نشاط SCM إعدادات شاشة مختلفة عن الحاوية (على سبيل المثال، تغيير حجم الشاشة أو نقل النشاط إلى شاشة أخرى)، تكون قيمة ActivityRecord#inSizeCompatMode() صحيحة ويتلقّى SizeCompatModeActivityController (في واجهة مستخدم النظام) callback لعرض زر إعادة تشغيل العملية.

أحجام الشاشة ونسبها العرض إلى الارتفاع

يتيح نظام التشغيل Android 10 استخدام نسب عرض إلى ارتفاع جديدة، بدءًا من نسب العرض إلى الارتفاع العالية للشاشات الطويلة والرفيعة وصولاً إلى نسب العرض إلى الارتفاع 1:1. يمكن للتطبيقات تحديد ApplicationInfo#maxAspectRatio وApplicationInfo#minAspectRatio الشاشة التي يمكنها التعامل معها.

نسب التطبيقات في Android 10

الشكل 1: أمثلة على نسب التطبيقات المتوافقة مع Android 10

يمكن أن تتضمّن عمليات تنفيذ الأجهزة شاشات ثانوية بأحجام ودقة أقل من تلك المطلوبة لنظام التشغيل Android 9 والإصدارات الأقدم (بحد أدنى 2.5 بوصة للعرض أو الارتفاع، وبحد أدنى 320 نقطة لكل بوصة لشاشة smallestScreenWidth)، ولكن لا يمكن عرض سوى الأنشطة التي تم تفعيلها للتوافق مع هذه الشاشات الصغيرة.

يمكن للتطبيقات تفعيل هذه الميزة من خلال تحديد الحد الأدنى للحجم المتوافق الذي يكون أصغر من أو يساوي حجم الشاشة المستهدَف. استخدِم سمتَي android:minHeight و android:minWidth لتنسيق النشاط فيملف AndroidManifest لإجراء ذلك.

سياسات العرض

يفصل نظام التشغيل Android 10 وينقل بعض سياسات العرض من التنفيذ التلقائي لWindowManagerPolicy في PhoneWindowManager إلى فئات لكل شاشة، مثل:

  • حالة الشاشة ودوران الشاشة
  • تتبُّع بعض المفاتيح وأحداث الحركة
  • واجهة مستخدم النظام ونوافذ الزينة

في الإصدار 9 من نظام Android (والإصدارات الأقدم)، كانت فئة PhoneWindowManager تعالج سياسات العرض والحالة والإعدادات والدوران وتتبُّع إطار النافذة الزخرفية وغيرها. ينقل نظام التشغيل Android 10 معظم هذه البيانات إلى الفئة DisplayPolicy، باستثناء تتبُّع التدوير الذي تم نقله إلى DisplayRotation.

إعدادات نافذة العرض

في Android 10، تم توسيع نطاق إعدادات النافذة التي يمكن ضبطها لكل شاشة لتشمل ما يلي:

  • وضع عرض المحتوى في النافذة التلقائي
  • قيم "التمويه"
  • تبديل المستخدمين ووضع التبديل
  • الحجم والكثافة ووضع تغيير الحجم الإجباري
  • وضع إزالة المحتوى (عند إزالة الشاشة)
  • إتاحة زخارف النظام وطريقة إدخال النص

تحتوي فئة DisplayWindowSettings على إعدادات لهذه الخيارات. ويتم الاحتفاظ بها على القرص في قسم /data في display_settings.xml في كل مرة يتم فيها تغيير أحد الإعدادات. للاطّلاع على التفاصيل، يُرجى الاطّلاع على DisplayWindowSettings.AtomicFileStorage و DisplayWindowSettings#writeSettings(). يمكن لصنّاع الأجهزة تقديم قيم تلقائية في display_settings.xml لإعدادات أجهزتهم. ومع ذلك، بما أنّ الملف مخزّن في /data، قد تكون هناك حاجة إلى منطق إضافي لاستعادة الملف في حال محوه من خلال عملية محو.

يستخدم نظام التشغيل Android 10 تلقائيًا DisplayInfo#uniqueId كمعرّف لشاشة عند تثبيت الإعدادات. يجب تعبئة uniqueId لجميع شاشات العرض. بالإضافة إلى ذلك، يُعدّ الإصدار مستقرًا للشاشات الخارجية وتلك المعروضة على الشبكة. من الممكن أيضًا استخدام منفذ شاشة خارجية كمعرّف، ويمكن ضبطه في DisplayWindowSettings#mIdentifier. عند كل عملية كتابة، تتم كتابة جميع الإعدادات ليكون من الآمن تعديل المفتاح المستخدَم لعرض إدخال في مساحة التخزين. لمعرفة التفاصيل، يُرجى الاطّلاع على معرّفات العرض الثابتة.

يتم الاحتفاظ بالإعدادات في دليل /data لأسباب متعلقة بالسجلّ. وكانت هذه الإعدادات تُستخدَم في الأصل للحفاظ على الإعدادات التي يضبطها المستخدم، مثل تبديل الاتجاه في الشاشة.

معرّفات العرض الثابتة

لم يقدّم الإصدار 9 من نظام Android (والإصدارات الأقدم) معرّفات ثابتة للشاشات في الإطار. عند إضافة شاشة إلى النظام، تم إنشاء Display#mDisplayId أو DisplayInfo#displayId لتلك الشاشة من خلال زيادة عداد ثابت. إذا أضاف النظام الشاشة نفسها وأزالها، تم إنشاء معرّف مختلف.

إذا كان الجهاز يتضمّن شاشات متعددة متاحة من بدء التشغيل، يمكن تحديد معرّفات مختلفة للشاشات، استنادًا إلى التوقيت. على الرغم من أنّ الإصدار 9 من Android (وإصدارات Android الأقدم) يتضمّن 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، وهو معرّف شاشة ثابت عند إعادة التشغيل. في الإصدار DisplayAddress من Android 10، تتوفّر شاشات DisplayAddress المخصّصة للأجهزة المزوّدة بشاشات داخلية وخارجية. يحتوي DisplayAddress.Physical على معرّف عرض ثابت (كما هو الحال في uniqueId) ويمكن إنشاؤه باستخدام DisplayAddress#fromPhysicalDisplayId().

يقدّم نظام التشغيل Android 10 أيضًا طريقة ملائمة للحصول على معلومات المنفذ (Physical#getPort()). ويمكن استخدام هذه الطريقة في الإطار الأساسي لتحديد الشاشات بشكل ثابت. على سبيل المثال، يتم استخدامه في DisplayWindowSettings). يحتوي DisplayAddress.Network على عنوان MAC ويمكن إنشاؤه باستخدام DisplayAddress#fromMacAddress().

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

استنادًا إلى معرّف شاشة HWC (الذي يمكن أن يكون غير شفاف وغير ثابت في بعض الأحيان)، تعرض هذه المحاولة رقم المنفذ المكوّن من 8 بت (الخاص بالمنصة) الذي يحدِّد الموصّل المادي لإخراج الشاشة، بالإضافة إلى ملف EDID للشاشة. يستخرج SurfaceFlinger معلومات الشركة المصنّعة أو الطراز من EDID لمحاولة إنشاء أرقام تعريف ثابتة للشاشة بسعة 64 بت يتم عرضها على إطار العمل. إذا كانت هذه الطريقة غير متوافقة أو أدّت إلى ظهور أخطاء، يعود SurfaceFlinger إلى وضع MD القديم، حيث يكون 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"

استخدام أكثر من شاشتَين

في الإصدار 9 من Android (والإصدارات الأقدم)، افترضت SurfaceFlinger وDisplayManagerService توفُّر شاشتَين أصليتين كحد أقصى بمعرّفات برمجية ثابتة 0 و1.

بدءًا من Android 10، يمكن لواجهة SurfaceFlinger الاستفادة من واجهة برمجة التطبيقات Hardware Composer (HWC) لإنشاء أرقام تعريف ثابتة للشاشة، ما يتيح لها إدارة عدد عشوائي من الشاشات. لمزيد من المعلومات، يُرجى الاطّلاع على معرّفات الإعلانات الثابتة.

يمكن للإطار البحث عن رمز IBinder المميّز لشاشة أصلية من خلال SurfaceControl#getPhysicalDisplayToken بعد الحصول على معرّف الشاشة المكوّن من 64 بت من SurfaceControl#getPhysicalDisplayIds أو من حدث hotplug في DisplayEventReceiver.

في الإصدار 10 من Android (والإصدارات الأقدم)، تكون الشاشة الداخلية الأساسية TYPE_INTERNAL ويتم وضع علامة على جميع الشاشات الثانوية على أنّها TYPE_EXTERNAL بغض النظر عن نوع الاتصال. لذلك، يتم التعامل مع الشاشات الداخلية الإضافية على أنّها شاشات خارجية. كحل بديل، يمكن للرمز المخصّص للجهاز أن يفترض DisplayAddress.Physical#getPort إذا كان معرّف الجهاز الثابت معروفًا وكان منطق تخصيص المنفذ قابلاً للتوقّع.

تمت إزالة هذا القيد في الإصدار 11 من Android (والإصدارات الأحدث).

  • في Android 11، تكون الشاشة الأولى التي يتم الإبلاغ عنها أثناء عملية التمهيد هي الشاشة الأساسية. لا يهم نوع الاتصال (داخلي مقابل خارجي). ومع ذلك، يبقى صحيحًا أنّه لا يمكن فصل الشاشة الأساسية، وبالتالي يجب أن تكون شاشة داخلية. يُرجى العِلم أنّ بعض الهواتف القابلة للطي تحتوي على عدة شاشات داخلية.
  • يتم تصنيف الشاشات الثانوية بشكل صحيح على أنّها Display.TYPE_INTERNAL أو Display.TYPE_EXTERNAL (المعروفة سابقًا باسم Display.TYPE_BUILT_IN وDisplay.TYPE_HDMI، على التوالي) استنادًا إلى نوع الاتصال.

التنفيذ

في الإصدار 9 من نظام التشغيل Android والإصدارات الأقدم، يتم تحديد الشاشات باستخدام أرقام تعريف 32 بت، ويمثّل الرمز 0 الشاشة الداخلية، ويمثّل الرمز 1 الشاشة الخارجية، ويمثّل الرمز [2, INT32_MAX] الشاشات الافتراضية التي تستخدم تكنولوجيا "العرض عالي الدقة"، ويمثّل الرمز -1 شاشة غير صالحة أو شاشة افتراضية لا تستخدم تكنولوجيا "العرض عالي الدقة".

بدءًا من Android 10، يتم منح الشاشات أرقام تعريف ثابتة ودائمة، ما يسمح لـ SurfaceFlinger وDisplayManagerService بتتبُّع أكثر من شاشتَين والتعرّف على الشاشات التي سبق أن تم عرضها. إذا كان IComposerClient.getDisplayIdentificationData متوافقًا مع واجهة برمجة التطبيقات لوحدة التحكّم في الشاشة (HWC) ويقدّم بيانات IComposerClient.getDisplayIdentificationData لتحديد الشاشة، سيحلِّل SurfaceFlinger بنية EDID ويخصّص معرّفات شاشة مستقرة بسعة 64 بت للشاشات الفعلية والشاشات الافتراضية لوحدة التحكّم في الشاشة (HWC). يتم التعبير عن الأرقام التعريفية باستخدام نوع خيار، حيث تمثّل القيمة الفارغة شاشة غير صالحة أو شاشة افتراضية غير متوافقة مع تقنية HWC. في حال عدم توفّر واجهة برمجة التطبيقات لعرض المحتوى على الشاشة (HWC)، يعود SurfaceFlinger إلى السلوك القديم مع استخدام شاشتَين أصليتين كحد أقصى.

التركيز على كل شاشة

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

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

لنفترض أنّ المستخدم يُدخل معلومات آمنة في حقل إدخال نص، ربما لتسجيل الدخول إلى تطبيق مصرفي أو إدخال نص يحتوي على معلومات حساسة. يمكن لأحد التطبيقات الضارة إنشاء شاشة افتراضية خارج الشاشة لتنفيذ نشاط ما، مع تضمين حقل إدخال نص أيضًا. يتم التركيز على الأنشطة المشروعة والضارة، ويعرض كلاهما مؤشر إدخال نشط (مؤشر سهم وامض).

ومع ذلك، بما أنّ الإدخال من لوحة مفاتيح (أجهزة أو برامج) يتم إدخاله في النشاط الأعلى فقط (التطبيق الذي تم تشغيله مؤخرًا)، يمكن للتطبيق الضار الاستيلاء على إدخال المستخدم من خلال إنشاء شاشة افتراضية مخفية، حتى عند استخدام لوحة مفاتيح برامج على شاشة الجهاز الأساسية.

استخدِم com.android.internal.R.bool.config_perDisplayFocusEnabled لضبط التركيز لكل شاشة.

التوافق

المشكلة: في الإصدار 9 من نظام Android والإصدارات الأقدم، يمكن التركيز على نافذة واحدة في النظام كحد أقصى في كل مرة.

الحل: في الحالة النادرة التي يتم فيها التركيز على نافذتَين من العملية نفسها، لا يقدّم النظام التركيز إلا على النافذة الأكثر ارتفاعًا في ترتيب Z. تتم إزالة هذا القيد على التطبيقات التي تستهدف الإصدار 10 من Android، وعندها من المتوقّع أن تتمكّن من إتاحة إمكانية التركيز على نوافذ متعدّدة في الوقت نفسه.

التنفيذ

تتحكّم WindowManagerService#mPerDisplayFocusEnabled في مدى توفّر هذه الميزة. في ActivityManager، يتم الآن استخدام ActivityDisplay#getFocusedStack() بدلاً من التتبّع العميق في متغيّر. ActivityDisplay#getFocusedStack() تحدد التركيز استنادًا إلى الترتيب حسب Z بدلاً من تخزين القيمة في ذاكرة التخزين المؤقت. ويعود سبب ذلك إلى أنّه لا يحتاج سوى مصدر واحد، وهو WindowManager، إلى تتبُّع ترتيب Z للأنشطة.

تتّبع أداة ActivityStackSupervisor#getTopDisplayFocusedStack() منهجًا مشابهًا في الحالات التي يجب فيها تحديد الحزمة التي تم التركيز عليها في النظام. يتمّ تنقّل الحِزم من الأعلى إلى الأسفل بحثًا عن أول حزمة مؤهّلة.

يمكن الآن أن يتضمّن InputDispatcher عدة نوافذ مُركّزة (نافذة واحدة لكل شاشة). إذا كان حدث الإدخال خاصًا بالشاشة، يتم إرساله إلى النافذة التي تم التركيز عليها في الشاشة المقابلة. بخلاف ذلك، يتم توجيهه إلى النافذة التي تم التركيز عليها في الشاشة التي تم التركيز عليها، وهي الشاشة التي تعامل معها المستخدم مؤخرًا.

يُرجى الاطّلاع على InputDispatcher::mFocusedWindowHandlesByDisplay و InputDispatcher::setFocusedDisplay(). تتم أيضًا تحديث التطبيقات التي تركّز عليها الرؤية بشكل منفصل في InputManagerService من خلال NativeInputManager::setFocusedApplication().

في WindowManager، يتم أيضًا تتبُّع النوافذ التي تم التركيز عليها بشكل منفصل. يُرجى الاطّلاع على DisplayContent#mCurrentFocus و DisplayContent#mFocusedApp وحالات الاستخدام المعنيّة. تم نقل طرق تتبُّع تعديلات التركيز المرتبط وتعديلها من WindowManagerService إلى DisplayContent.