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