پشتیبانی از نمایشگر

به‌روزرسانی‌های انجام‌شده در این بخش‌های ویژه نمایشگر در زیر ارائه شده است:

تغییر اندازه فعالیت‌ها و نمایشگرها

برای نشان دادن اینکه برنامه ممکن است از حالت چندپنجره‌ای یا تغییر اندازه پشتیبانی نکند، فعالیت‌ها از مشخصه resizeableActivity=false استفاده می‌کنند. مشکلات رایجی که برنامه‌ها هنگام تغییر اندازه فعالیت‌ها با آن‌ها مواجه می‌شوند عبارت‌اند از:

  • فعالیت می‌تواند پیکربندی متفاوتی از برنامه یا عنصر غیردیداری دیگری داشته باشد. یکی از اشتباهات رایج خواندن سنجه‌های نمایش از بافت برنامه است. مقادیر برگشتی با سنجه‌های ناحیه نمایان که فعالیت در آن نمایش داده می‌شود تعدیل نخواهد شد.
  • فعالیت ممکن است از تغییر اندازه پشتیبانی نکند و ازکار بیفتد، رابط کاربری را به‌صورت تحریف‌شده نمایش دهد، یا به‌دلیل راه‌اندازی مجدد بدون ذخیره کردن وضعیت نمونه، وضعیت خود را ازدست بدهد.
  • ممکن است برنامه‌ای تلاش کند از مختصات ورودی مطلق (به‌جای مختصات مرتبط با موقعیت پنجره) استفاده کند که ممکن است ورودی را در حالت چندپنجره‌ای خراب کند.

در Android 7 (و نسخه‌های بالاتر)، می‌توان برنامه‌ای را تنظیم کرد resizeableActivity=false تا همیشه در حالت تمام‌صفحه اجرا شود. در این حالت، پلاتفرم از رفتن فعالیت‌های غیرقابل تغییر اندازه به صفحه دونیمه جلوگیری می‌کند. اگر کاربر بخواهد فعالیتی غیرقابل‌تغییر اندازه را از راه‌انداز در حالی‌که در حالت صفحه تقسیم‌شده است فراخوانی کند، پلاتفرم از حالت صفحه تقسیم‌شده خارج می‌شود و فعالیت غیرقابل‌تغییر اندازه را در حالت تمام‌صفحه راه‌اندازی می‌کند.

برنامه‌هایی که این مشخصه را به‌طور صریح در مانیفست روی false تنظیم می‌کنند نباید در حالت چندپنجره‌ای راه‌اندازی شوند، مگر اینکه حالت سازگاری اعمال شود:

  • همین پیکربندی بر فرایند اعمال می‌شود که شامل همه فعالیت‌ها و مؤلفه‌های غیرفعالیت است.
  • پیکربندی اعمال‌شده الزامات CDD را برای نمایشگرهای سازگار با برنامه برآورده می‌کند.

در Android 10، پلاتفرم همچنان از رفتن فعالیت‌های تغییرناپذیر به حالت صفحهٔ دونیمه جلوگیری می‌کند، اما اگر فعالیت جهت یا نسبت ابعادی ثابتی را اعلام کرده باشد، می‌توان آن را به‌طور موقت مقیاس‌بندی کرد. اگر نه، فعالیت تغییر اندازه می‌دهد تا کل صفحه‌نمایش را پر کند، مانند Android نسخه ۹ و پایین‌تر.

پیاده‌سازی پیش‌فرض خط‌مشی زیر را اعمال می‌کند:

وقتی فعالیتی ازطریق استفاده از ویژگی android:resizeableActivity به‌عنوان ناسازگار با چندپنجره‌ای اعلام می‌شود و وقتی آن فعالیت یکی از شرایط شرح‌داده‌شده در زیر را برآورده می‌کند، در این صورت وقتی پیکربندی صفحه اعمال‌شده باید تغییر کند، فعالیت و فرایند با پیکربندی اصلی ذخیره می‌شود و کاربر برای راه‌اندازی مجدد فرایند برنامه برای استفاده از پیکربندی صفحه به‌روزشده، امکاناتی دریافت می‌کند.

  • جهت ثابت ازطریق برنامه android:screenOrientation
  • برنامه با هدف‌یابی سطح API دارای حداکثر یا حداقل نسبت ابعادی پیش‌فرض است یا نسبت ابعادی را به‌طور صریح اعلام می‌کند

این شکل فعالیت غیرقابل‌تغییر اندازه با نسبت ابعادی اعلام‌شده را نشان می‌دهد. هنگام تا کردن دستگاه، پنجره کوچک می‌شود تا با حفظ نسبت ابعادی و بااستفاده از تصویر عریض مناسب، در ناحیه موردنظر جا شود. علاوه‌براین، هر بار که ناحیه نمایش فعالیت تغییر می‌کند، گزینه بازراه‌اندازی فعالیت به کاربر ارائه می‌شود.

وقتی دستگاه را باز می‌کنید، پیکربندی، اندازه، و نسبت ابعادی فعالیت تغییر نمی‌کند، اما گزینه بازراه‌اندازی فعالیت نمایش داده می‌شود.

وقتی resizeableActivity تنظیم نشده باشد (یا روی true تنظیم شده باشد)، برنامه کاملاً از تغییر اندازه پشتیبانی می‌کند.

پیاده‌سازی

فعالیت غیرقابل‌تغییر اندازه با جهت‌گیری یا نسبت ابعادی ثابت در کد، حالت سازگاری اندازه (SCM) نامیده می‌شود. شرط در ActivityRecord#shouldUseSizeCompatMode() تعریف شده است. وقتی فعالیت SCM راه‌اندازی می‌شود، پیکربندی مربوط به صفحه‌نمایش (مثل اندازه یا تراکم) در پیکربندی ملغی‌سازی درخواست‌شده ثابت می‌شود، بنابراین فعالیت دیگر به پیکربندی نمایشگر فعلی وابسته نیست.

اگر فعالیت SCM نتواند کل صفحه را پر کند، در بالا تراز می‌شود و به‌صورت افقی در مرکز قرار می‌گیرد. محدوده‌های فعالیت توسط AppWindowToken#calculateCompatBoundsTransformation() محاسبه می‌شود.

وقتی فعالیت SCM از پیکربندی صفحه‌نمایش متفاوتی نسبت‌به ظرف خود استفاده می‌کند (برای مثال، نمایشگر تغییر اندازه می‌دهد یا فعالیت به نمایشگر دیگری منتقل می‌شود)، ActivityRecord#inSizeCompatMode() درست است و SizeCompatModeActivityController (در «میانای کاربری سیستم») بازخوان را برای نمایش دکمه بازراه‌اندازی فرایند دریافت می‌کند.

اندازه‌های نمایشگر و نسبت‌های ابعادی

‫Android 10 از نسبت‌های ابعادی جدید از نسبت‌های بالای صفحه‌های بلند و باریک تا نسبت‌های ۱:۱ پشتیبانی می‌کند. برنامه‌ها می‌توانند ApplicationInfo#maxAspectRatio و ApplicationInfo#minAspectRatio صفحه را که می‌توانند مدیریت کنند تعریف کنند.

نسبت‌های برنامه در Android 10

شکل ۱. نمونه نسبت‌های برنامه پشتیبانی‌شده در Android 10

پیاده‌سازی‌های دستگاه می‌تواند نمایشگرهای ثانویه با اندازه‌ها و وضوح‌های کوچک‌تر از آنچه Android 9 و نسخه‌های پایین‌تر نیاز دارند داشته باشد (حداقل ۲٫۵ اینچ عرض یا ارتفاع، حداقل ۳۲۰ DP برای smallestScreenWidth)، اما فقط فعالیت‌هایی که برای پشتیبانی از این نمایشگرهای کوچک موافقت می‌کنند می‌توانند در آنجا قرار بگیرند.

برنامه‌ها می‌توانند با اعلام حداقل اندازه پشتیبانی‌شده که کوچک‌تر از یا برابر با اندازه نمایشگر هدف است، موافقت خود را اعلام کنند. برای انجام این کار، از مشخصه‌های چیدمان فعالیت 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 نسخه ۹ (و پایین‌تر) شناسه‌های پایداری برای نمایشگرها در چارچوب ارائه نمی‌کرد. وقتی نمایشگری به سیستم اضافه می‌شود، 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 نسخه ۱۰ همچنین روشی مناسب برای دریافت اطلاعات درگاه (Physical#getPort()) ارائه می‌دهد. از این روش می‌توان در چارچوب برای شناسایی ایستا نمایشگرها استفاده کرد. برای مثال، در DisplayWindowSettings استفاده می‌شود. DisplayAddress.Network حاوی نشانی MAC است و می‌تواند با DisplayAddress#fromMacAddress() ایجاد شود.

این افزوده‌ها به سازندگان دستگاه امکان می‌دهد نمایشگرها را در چیدمان‌های چندنمایشگری ثابت شناسایی کنند و تنظیمات و ویژگی‌های مختلف سیستم را بااستفاده از شناسه‌های نمایشگر ثابت، مثل درگاه‌های نمایشگرهای فیزیکی، پیکربندی کنند. این روش‌ها پنهان هستند و فقط برای استفاده در system_server درنظر گرفته شده‌اند.

با درنظر گرفتن شناسه نمایشگر HWC (که می‌تواند غیرشفاف باشد و همیشه پایدار نیست)، این روش شماره درگاه ۸ بیتی (ویژه پلاتفرم) را که یک اتصال‌دهنده فیزیکی برای برونداد نمایشگر را شناسایی می‌کند، و همچنین داده‌های EDID نمایشگر را برمی‌گرداند. ‫SurfaceFlinger اطلاعات سازنده یا مدل را از EDID استخراج می‌کند تا شناسه‌های نمایشگر ۶۴ بیتی پایداری را که در چارچوب نمایش داده می‌شود تولید کند. اگر این روش پشتیبانی نشود یا خطا دهد، 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 وجود حداکثر دو نمایشگر فیزیکی با شناسه‌های سخت‌کدگذاری‌شده ۰ و ۱ را فرض می‌کردند.

از Android 10،‏ SurfaceFlinger می‌تواند از میانای برنامه‌سازی کاربردی Hardware Composer (HWC) برای تولید شناسه‌های نمایشگر پایدار استفاده کند، که به آن امکان می‌دهد تعداد دلخواهی از نمایشگرهای فیزیکی را مدیریت کند. برای کسب اطلاعات بیشتر، به شناسه‌های نمایشگر ثابت مراجعه کنید.

چارچوب می‌تواند ازطریق SurfaceControl#getPhysicalDisplayToken پس‌از دریافت شناسه نمایشگر ۶۴ بیتی از SurfaceControl#getPhysicalDisplayIds یا از رویداد اتصال سریع DisplayEventReceiver، نشان IBinder را برای نمایشگر فیزیکی جستجو کند.

در Android 10 (و نسخه‌های پایین‌تر)، نمایشگر داخلی اصلی TYPE_INTERNAL است و همه نمایشگرهای فرعی به‌عنوان TYPE_EXTERNAL بدون درنظر گرفتن نوع اتصال پرچم‌گذاری می‌شوند. بنابراین، نمایشگرهای داخلی اضافی به‌عنوان نمایشگر خارجی درنظر گرفته می‌شوند. به‌عنوان راهکار موقت، اگر HWC شناخته‌شده باشد و منطق تخصیص درگاه قابل‌پیش‌بینی باشد، کد مختص دستگاه می‌تواند درباره DisplayAddress.Physical#getPort فرضیاتی داشته باشد.

این محدودیت در Android نسخه ۱۱ (و بالاتر) برداشته شده است.

  • در Android 11، اولین نمایشگری که درطول راه‌اندازی گزارش می‌شود نمایشگر اصلی است. نوع اتصال (داخلی دربرابر خارجی) نامربوط است. بااین‌حال، این موضوع همچنان درست است که نمایشگر اصلی نمی‌تواند قطع شود و در عمل باید نمایشگر داخلی باشد. توجه داشته باشید که برخی‌از تلفن‌های تاشو چند نمایشگر داخلی دارند.
  • نمایشگرهای ثانویه بسته به نوع اتصالشان به‌درستی به‌عنوان Display.TYPE_INTERNAL یا Display.TYPE_EXTERNAL (که قبلاً به‌ترتیب به‌عنوان Display.TYPE_BUILT_IN و Display.TYPE_HDMI شناخته می‌شدند) دسته‌بندی می‌شوند.

پیاده‌سازی

در Android 9 و نسخه‌های پایین‌تر، نمایشگرها با شناسه‌های ۳۲ بیتی شناسایی می‌شوند، که در آن ۰ نمایشگر داخلی، ۱ نمایشگر خارجی، [2, INT32_MAX] نمایشگرهای مجازی HWC است، و ۱- نشان‌دهنده نمایشگر نامعتبر یا نمایشگر مجازی غیرHWC است.

از Android 10، به نمایشگرها شناسه‌های پایدار و دائمی داده می‌شود که به SurfaceFlinger و DisplayManagerService اجازه می‌دهد بیش‌از دو نمایشگر را ردیابی کنند و نمایشگرهایی را که قبلاً دیده‌اند تشخیص دهند. اگر HWC از IComposerClient.getDisplayIdentificationData پشتیبانی کند و داده‌های شناسایی نمایشگر ارائه دهد، SurfaceFlinger ساختار EDID را تجزیه می‌کند و شناسه‌های نمایشگر پایدار ۶۴ بیتی را برای نمایشگرهای مجازی 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 منتقل شده است.