תמיכה בתצוגה

עדכונים שבוצעו לאזורים הספציפיים האלה לתצוגה מסופקים להלן:

שנה את גודל הפעילויות והתצוגות

כדי לציין שייתכן שאפליקציה לא תומכת במצב ריבוי חלונות או שינוי גודל, פעילויות משתמשות בתכונה resizeableActivity=false . בעיות נפוצות בהן נתקלים אפליקציות כאשר גודל הפעילויות משתנים כוללות:

  • לפעילות יכולה להיות תצורה שונה מהאפליקציה או מרכיב אחר שאינו ויזואלי. טעות נפוצה היא לקרוא מדדי תצוגה מהקשר האפליקציה. הערכים המוחזרים לא יותאמו למדדי האזור הגלויים שבהם מוצגת פעילות.
  • פעילות עשויה שלא לטפל בשינוי גודל ולקרס, להציג ממשק משתמש מעוות או לאבד מצב עקב הפעלה מחדש מבלי לשמור את מצב המופע.
  • אפליקציה עשויה לנסות להשתמש בקואורדינטות קלט מוחלטות (במקום אלה ביחס למיקום החלון), מה שעלול לשבור את הקלט בריבוי חלונות.

באנדרואיד 7 (ומעלה), ניתן להגדיר אפליקציה resizeableActivity=false כך שתפעל תמיד במצב מסך מלא. במקרה זה, הפלטפורמה מונעת מפעילויות שאינן ניתנות לשינוי גודל להיכנס למסך מפוצל. אם המשתמש מנסה להפעיל פעילות שאינה ניתנת לשינוי גודל מהמשגר ​​בעודו נמצא במצב מסך מפוצל, הפלטפורמה יוצאת ממצב מסך מפוצל ומשיקה את הפעילות שאינה ניתנת לשינוי גודל במצב מסך מלא.

אין להפעיל אפליקציות שמגדירות את התכונה הזו ל- false במניפסט במצב ריבוי חלונות, אלא אם מוחל מצב התאימות:

  • אותה תצורה מוחלת על התהליך, המכיל את כל הפעילויות והרכיבים שאינם פעילות.
  • התצורה המיושמת עומדת בדרישות ה-CDD עבור צגים תואמים לאפליקציה.

באנדרואיד 10, הפלטפורמה עדיין מונעת מפעילויות שאינן ניתנות לשינוי גודל לעבור למצב מסך מפוצל, אך ניתן להתאים אותן באופן זמני אם הפעילות הכריזה על כיוון קבוע או יחס רוחב-גובה. אם לא, הפעילות משתנה כדי למלא את כל המסך כמו באנדרואיד 9 ומטה.

יישום ברירת המחדל מחיל את המדיניות הבאה:

כאשר פעילות שהוכרזה כלא תואמת לריבוי חלונות באמצעות התכונה android:resizeableActivity וכאשר פעילות זו עומדת באחד מהתנאים המתוארים להלן, אז כאשר תצורת המסך החלה חייבת להשתנות, הפעילות והתהליך נשמרים עם התצורה המקורית ולמשתמש ניתנת הזדמנות להפעיל מחדש את תהליך האפליקציה כדי להשתמש בתצורת המסך המעודכנת.

  • האם אוריינטציה קבועה באמצעות היישום של android:screenOrientation
  • לאפליקציה יש ברירת מחדל של יחס רוחב-גובה מקסימלי או מינימלי על ידי מיקוד לרמת API או מצהירה במפורש על יחס רוחב-גובה

איור זה מציג פעילות שאינה ניתנת לשינוי גודל עם יחס רוחב-גובה מוצהר. בעת קיפול המכשיר, החלון מצטמצם כך שיתאים לאזור תוך שמירה על יחס הגובה-רוחב באמצעות ה-letterboxing המתאים. בנוסף, ניתנת למשתמש אפשרות הפעלה מחדש בכל פעם שאזור התצוגה של הפעילות משתנה.

בעת פתיחת המכשיר, התצורה, הגודל ויחס הגובה-רוחב של הפעילות אינם משתנים, אך מוצגת האפשרות להפעיל מחדש את הפעילות.

כאשר resizeableActivity לא מוגדר (או שהוא מוגדר כ- true ), האפליקציה תומכת באופן מלא בשינוי גודל.

יישום

פעילות שאינה ניתנת לשינוי גודל עם כיוון קבוע או יחס רוחב-גובה נקרא בקוד מצב תאימות גודל (SCM). התנאי מוגדר ב- ActivityRecord#shouldUseSizeCompatMode() . כאשר פעילות SCM מופעלת, התצורה הקשורה למסך (כגון גודל או צפיפות) קבועה בתצורת העקיפה המבוקשת, כך שהפעילות אינה תלויה עוד בתצורת התצוגה הנוכחית.

אם פעילות SCM לא יכולה למלא את כל המסך, היא מיושרת למעלה וממרכזת אופקית. גבולות הפעילות מחושבים על ידי AppWindowToken#calculateCompatBoundsTransformation() .

כאשר פעילות SCM משתמשת בתצורת מסך שונה מהמכיל שלה (לדוגמה, גודל התצוגה שונה, או פעילות הועברה לתצוגה אחרת), ActivityRecord#inSizeCompatMode() היא אמת ו- SizeCompatModeActivityController (בממשק המשתמש של המערכת) מקבל את ההתקשרות חזרה כדי להציג את התהליך לחצן הפעלה מחדש.

גדלי תצוגה ויחסי גובה-רוחב

אנדרואיד 10 מספקת תמיכה ביחסי רוחב-גובה חדשים מיחסים גבוהים של מסכים ארוכים ודקים ועד ליחסים של 1:1. אפליקציות יכולות להגדיר את ApplicationInfo#maxAspectRatio ואת ApplicationInfo#minAspectRatio של המסך שהם מסוגלים לטפל בהם.

יחסי אפליקציה באנדרואיד 10

איור 1. יחסי אפליקציות לדוגמה נתמכים ב-Android 10

למימושים של מכשירים יכולים להיות צגים משניים עם גדלים ורזולוציות קטנות מאלה הנדרשות על-ידי אנדרואיד 9, ומטה (מינימום של 2.5 אינץ' רוחב או גובה, מינימום של 320 DP עבור smallestScreenWidth ), אך ניתן לבצע רק פעילויות שמצטרפות לתמוך בצגים קטנים אלה. מוצב שם.

אפליקציות יכולות להצטרף על ידי הצהרה על גודל מינימלי נתמך הקטן מ-oe שווה לגודל התצוגה היעד. השתמש בתכונות פריסת הפעילות android:minHeight ו- android:minWidth ב-AndroidManifest כדי לעשות זאת.

מדיניות תצוגה

אנדרואיד 10 מפרידה ומעבירה מדיניות תצוגה מסוימת מיישום ברירת המחדל WindowManagerPolicy ב- PhoneWindowManager למחלקות לכל תצוגה, כגון:

  • מצב תצוגה וסיבוב
  • כמה מקשים ומעקב אחר אירועי תנועה
  • ממשק משתמש וחלונות דקורציה של המערכת

באנדרואיד 9 (ומטה), מחלקת PhoneWindowManager טיפלה במדיניות תצוגה, מצב והגדרות, סיבוב, מעקב אחר מסגרות חלונות קישוט ועוד. אנדרואיד 10 מעביר את רוב זה למחלקה DisplayPolicy , למעט מעקב אחר סיבוב, שהועבר ל- DisplayRotation .

הגדרות חלון תצוגה

באנדרואיד 10, הגדרת החלונות הניתנת להגדרה לכל תצוגה הורחבה כך שתכלול:

  • מצב חלונות תצוגה ברירת מחדל
  • סריקת יתר של ערכים
  • מצב סיבוב וסיבוב משתמש
  • מצב גודל, צפיפות וקנה מידה מאולץ
  • מצב הסרת תוכן (כאשר התצוגה מוסרת)
  • תמיכה בקישוטי מערכת וב-IME

המחלקה DisplayWindowSettings מכילה הגדרות עבור אפשרויות אלה. הם מועברים לתקליטור במחיצת /data ב- display_settings.xml בכל פעם ששינוי הגדרה. לפרטים, ראה DisplayWindowSettings.AtomicFileStorage ו- DisplayWindowSettings#writeSettings() . יצרני התקנים יכולים לספק ערכי ברירת מחדל ב- display_settings.xml עבור תצורת המכשיר שלהם. עם זאת, מכיוון שהקובץ מאוחסן ב- /data , ייתכן שיהיה צורך בהיגיון נוסף כדי לשחזר את הקובץ אם נמחק על ידי מחיקה.

כברירת מחדל, אנדרואיד 10 משתמש DisplayInfo#uniqueId כמזהה עבור תצוגה בעת שמירה על ההגדרות. יש לאכלס uniqueId עבור כל התצוגות. בנוסף, הוא יציב עבור תצוגות פיזיות ורשתות. אפשר גם להשתמש ביציאה של תצוגה פיזית כמזהה, שניתן להגדיר ב- DisplayWindowSettings#mIdentifier . בכל כתיבה, כל ההגדרות נכתבות כך שבטוח לעדכן את המפתח המשמש לכניסה לתצוגה באחסון. לפרטים, ראה מזהי תצוגה סטטיים .

ההגדרות נשארות בספריית /data מסיבות היסטוריות. במקור, הם שימשו לשמירה על הגדרות מוגדרות על ידי המשתמש, כגון סיבוב תצוגה.

מזהי תצוגה סטטיים

אנדרואיד 9 (ומטה) לא סיפק מזהים יציבים לתצוגות במסגרת. כאשר נוספה תצוגה למערכת, Display#mDisplayId או DisplayInfo#displayId נוצר עבור אותה תצוגה על ידי הגדלה של מונה סטטי. אם המערכת הוסיפה והסירה את אותה תצוגה, נוצר מזהה שונה.

אם למכשיר היו מספר צגים זמינים מהאתחול, ניתן היה להקצות לצגים מזהים שונים, בהתאם לתזמון. בעוד ש-Android 9 (וקודמתה יותר) כללה DisplayInfo#uniqueId , היא לא הכילה מספיק מידע כדי להבדיל בין צגים מכיוון שצגים פיזיים זוהו כ- local:0 או local:1 , כדי לייצג את התצוגה המובנית והחיצונית.

אנדרואיד 10 משנה את DisplayInfo#uniqueId כדי להוסיף מזהה יציב וכדי להבדיל בין תצוגות מקומיות, רשתות ווירטואליות.

סוג תצוגה פוּרמָט
מְקוֹמִי
local:<stable-id>
רֶשֶׁת
network:<mac-address>
וירטואלי
virtual:<package-name-and-name>

בנוסף לעדכונים של uniqueId , DisplayInfo.address מכיל DisplayAddress , מזהה תצוגה שיציב לאורך אתחולים מחדש. באנדרואיד 10, DisplayAddress תומכת בתצוגות פיזיות וברשתות. DisplayAddress.Physical מכיל מזהה תצוגה יציב (זהה לזה של uniqueId ) וניתן ליצור אותו עם DisplayAddress#fromPhysicalDisplayId() .

אנדרואיד 10 מספקת גם שיטה נוחה לקבל מידע על יציאות ( Physical#getPort() ). ניתן להשתמש בשיטה זו במסגרת לזיהוי סטטי של תצוגות. לדוגמה, הוא משמש ב- DisplayWindowSettings ). DisplayAddress.Network מכיל את כתובת ה-MAC וניתן ליצור באמצעות DisplayAddress#fromMacAddress() .

תוספות אלו מאפשרות ליצרני התקנים לזהות תצוגות בהגדרות מרובות תצוגה סטטיות ולהגדיר הגדרות ותכונות מערכת שונות באמצעות מזהי תצוגה סטטיים, כגון יציאות לצגים פיזיים. שיטות אלו מוסתרות והן מיועדות לשימוש רק בתוך system_server .

בהינתן מזהה תצוגה של HWC (שיכול להיות אטום ולא תמיד יציב), שיטה זו מחזירה את מספר יציאת 8 סיביות (הספציפית לפלטפורמה) המזהה מחבר פיזי לפלט תצוגה, כמו גם את ה-EDID של התצוגה. SurfaceFlinger מחלץ מידע יצרן או דגם מה-EDID כדי ליצור את מזהי התצוגה היציבים של 64 סיביות שנחשפו למסגרת. אם שיטה זו אינה נתמכת או שגיאה יוצאת דופן, SurfaceFlinger חוזר למצב MD מדור קודם, שבו DisplayInfo#address הוא null ו- 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 (ומטה), SurfaceFlinger ו- DisplayManagerService הניחו את קיומם של שני צגים פיזיים לכל היותר עם מזהים מקודדים קשיחים 0 ו-1.

החל מ-Android 10, SurfaceFlinger יכול למנף ממשק API של Hardware Composer (HWC) כדי ליצור מזהי תצוגה יציבים, המאפשרים לו לנהל מספר שרירותי של תצוגות פיזיות. למידע נוסף, ראה מזהי תצוגה סטטיים .

המסגרת יכולה לחפש את אסימון IBinder עבור תצוגה פיזית דרך SurfaceControl#getPhysicalDisplayToken לאחר השגת מזהה התצוגה של 64 סיביות מ- SurfaceControl#getPhysicalDisplayIds או מאירוע Hotplug של DisplayEventReceiver .

באנדרואיד 10 (ומטה), התצוגה הפנימית הראשית היא TYPE_INTERNAL וכל הצגים המשניים מסומנים כ- TYPE_EXTERNAL ללא קשר לסוג החיבור. לכן, תצוגות פנימיות נוספות מטופלות כאל חיצוניות. כפתרון עוקף, קוד ספציפי למכשיר יכול ליצור הנחות לגבי DisplayAddress.Physical#getPort אם ה-HWC ידוע והלוגיקה של הקצאת היציאה ניתנת לחיזוי.

מגבלה זו מוסרת באנדרואיד 11 (ומעלה).

  • באנדרואיד 11, התצוגה הראשונה שדווחה במהלך האתחול היא התצוגה הראשית. סוג החיבור (פנימי מול חיצוני) אינו רלוונטי. עם זאת, זה נשאר נכון שלא ניתן לנתק את התצוגה הראשית ומכאן שהיא חייבת להיות תצוגה פנימית בפועל. שימו לב שלחלק מהטלפונים המתקפלים יש מספר צגים פנימיים.
  • תצוגות משניות מסווגות כהלכה כ- Display.TYPE_INTERNAL או Display.TYPE_EXTERNAL (שנודעו בעבר כ- Display.TYPE_BUILT_IN ו- Display.TYPE_HDMI , בהתאמה) בהתאם לסוג החיבור שלהם.

יישום

באנדרואיד 9 ומטה, התצוגות מזוהות על ידי מזהי 32 סיביות, כאשר 0 הוא התצוגה הפנימית, 1 הוא התצוגה החיצונית, [2, INT32_MAX] הם צגים וירטואליים של HWC, ו-1 מייצג תצוגה לא חוקית או תצוגה שאינה של HWC תצוגה וירטואלית.

החל מאנדרואיד 10, לצגים מקבלים מזהים יציבים ומתמשכים, מה שמאפשר ל-SurfaceFlinger ול- DisplayManagerService לעקוב אחר יותר משתי תצוגות ולזהות תצוגות שנראו בעבר. אם ה-HWC תומך IComposerClient.getDisplayIdentificationData ומספק נתוני זיהוי תצוגה, SurfaceFlinger מנתח את מבנה ה-EDID ומקצה מזהי תצוגה יציבים של 64 סיביות עבור תצוגות וירטואליות פיזיות ו-HWC. המזהים באים לידי ביטוי באמצעות סוג אופציה, כאשר הערך null מייצג תצוגה לא חוקית או תצוגה וירטואלית שאינה של HWC. ללא תמיכת HWC, SurfaceFlinger חוזרת להתנהגות מדור קודם עם שני צגים פיזיים לכל היותר.

מיקוד לכל תצוגה

כדי לתמוך במספר מקורות קלט המכוונים לצגים בודדים בו-זמנית, ניתן להגדיר את אנדרואיד 10 לתמיכה במספר חלונות ממוקדים, לכל היותר אחד לכל תצוגה. זה מיועד רק לסוגים מיוחדים של מכשירים כאשר משתמשים מרובים מקיימים אינטראקציה עם אותו מכשיר בו-זמנית ומשתמשים בשיטות קלט או מכשירים שונים, כגון Android Automotive.

מומלץ מאוד שתכונה זו לא תהיה מופעלת עבור מכשירים רגילים, כולל מכשירים מרובי מסכים או כאלה המשמשים לחוויות דמויות שולחן עבודה. זה נובע בעיקר מחשש אבטחה שעלול לגרום למשתמשים לתהות באיזה חלון יש מיקוד קלט.

דמיינו את המשתמש שמזין מידע מאובטח לשדה קלט טקסט, אולי נכנס לאפליקציית בנק או מזין טקסט שמכיל מידע רגיש. אפליקציה זדונית עלולה ליצור תצוגה וירטואלית מחוץ למסך שבאמצעותה ניתן לבצע פעילות, גם עם שדה קלט טקסט. לפעילויות לגיטימיות וזדוניות יש מיקוד ושתיהן מציגות מחוון קלט פעיל (סמן מהבהב).

עם זאת, מכיוון שקלט ממקלדת (חומרה או תוכנה) מוזן לפעילות העליונה בלבד (אותה אפליקציה שהושקה לאחרונה), על ידי יצירת תצוגה וירטואלית נסתרת, אפליקציה זדונית עלולה לתפוס קלט משתמש, גם בעת שימוש במקלדת תוכנה בתצוגת המכשיר הראשי.

השתמש com.android.internal.R.bool.config_perDisplayFocusEnabled כדי להגדיר מיקוד לכל תצוגה.

תְאִימוּת

בעיה: באנדרואיד 9 ומטה, לכל היותר לחלון אחד במערכת יש פוקוס בכל פעם.

פתרון: במקרה הנדיר שבו יתמקדו שני חלונות מאותו תהליך, המערכת מספקת מיקוד רק לחלון הגבוה יותר בסדר ה-Z. הגבלה זו מוסרת עבור אפליקציות המכוונות לאנדרואיד 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 .