دعم العرض

يتم توفير التحديثات التي تم إجراؤها على هذه المناطق الخاصة بالعرض أدناه:

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

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

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

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

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

  • يتم تطبيق نفس التكوين على العملية، التي تحتوي على كافة الأنشطة والمكونات غير المتعلقة بالنشاط.
  • يفي التكوين المطبق بمتطلبات CDD للشاشات المتوافقة مع التطبيق.

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

يطبق التنفيذ الافتراضي السياسة التالية:

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

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

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

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

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

تطبيق

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

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

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

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

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

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

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

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

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

عرض السياسات

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

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

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

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

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

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

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

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

نظرًا لمعرف عرض 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"

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

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

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

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

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

تمت إزالة هذا القيد في 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] هي شاشات HWC الافتراضية، ويمثل -1 شاشة غير صالحة أو شاشة غير تابعة لـ HWC العرض الظاهري.

بدءًا من Android 10، يتم منح الشاشات معرفات ثابتة ومستمرة، مما يسمح لـ SurfaceFlinger و DisplayManagerService بتتبع أكثر من شاشتين والتعرف على الشاشات التي تمت مشاهدتها مسبقًا. إذا كان HWC يدعم IComposerClient.getDisplayIdentificationData ويوفر بيانات تعريف العرض، فسيقوم SurfaceFlinger بتوزيع بنية EDID وتخصيص معرفات عرض ثابتة 64 بت لشاشات العرض الفعلية وشاشات HWC الافتراضية. يتم التعبير عن المعرفات باستخدام نوع خيار، حيث تمثل القيمة الخالية عرضًا غير صالح أو عرضًا افتراضيًا غير تابع لشركة HWC. بدون دعم HWC، يعود 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() . يتم أيضًا تحديث التطبيقات المركزة بشكل منفصل في InputManagerService من خلال NativeInputManager::setFocusedApplication() .

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