إظهار الدعم

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

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

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

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

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

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

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

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

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

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

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

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

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

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

التنفيذ

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

إذا لم يتمكن نشاط 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.xml لإجراء ذلك.

سياسات العرض

يفصل 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 لأسباب سابقة. وكانت تُستخدَم في الأصل للحفاظ على الإعدادات التي يضبطها المستخدم، مثل تبديل الاتجاه في الشاشة.

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

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

إذا كان الجهاز يتضمّن عدة شاشات متاحة من بدء التشغيل، يمكن تحديد معرّفات مختلفة للشاشات، استنادًا إلى التوقيت. على الرغم من أنّ الإصدار 9 من Android (وإصدارات Android الأقدم) يتضمّن DisplayInfo#uniqueId، إلا أنّه لا يحتوي على معلومات كافية للتفريق بين الشاشات لأنّه تم تحديد الشاشات الخارجية على أنّها local:0 أو local:1، لتمثيل الشاشة المدمجة والشاشة الخارجية.

يغيّر نظام Android 10 DisplayInfo#uniqueId لإضافة معرّف ثابت والتمييز بين الشاشات المحلية والشاشات والافتراضية.

نوع العرض التنسيق
بالتوقيت المحلي
local:<stable-id>
الشبكة
network:<mac-address>
"Virtual" (افتراضي)
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().

تسمح هذه الإضافات لصنّاع الأجهزة بتحديد الشاشات في عمليات الإعداد الثابتة للشاشات المتعدّدة وضبط إعدادات النظام والميزات المختلفة باستخدام معرّفات الشاشات الثابتة، مثل منافذ الشاشات الخارجية. هذه الطرق مخفية والغرض منها الاستخدام فقط ضمن 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.

في نظام التشغيل 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، على التوالي) استنادًا إلى نوع الاتصال.

التنفيذ

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

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

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

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

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

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

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

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

التوافق

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

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

التنفيذ

تتحكّم 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.