בהמשך מפורטים העדכונים שבוצעו באזורים הספציפיים האלה של המסך:
- שינוי הגודל של פעילויות ותצוגות
- גדלי מסך ויחסי גובה-רוחב
- מדיניות בנושא מודעות לרשת המדיה
- הגדרות של חלון התצוגה
- מזהים סטטיים של מודעות לרשת המדיה
- שימוש ביותר משני מסכים
- התמקדות בכל מסך
שינוי הגודל של פעילויות ומסכים
כדי לציין שאפליקציה לא תומכת במצב מרובה חלונות או בשינוי גודל, משתמשים בפעילויות במאפיין resizeableActivity=false. בעיות נפוצות שנתקלים בהן באפליקציות כשמשנים את הגודל של הפעילויות כוללות:
- הפעילות יכולה להיות מוגדרת באופן שונה מהאפליקציה או מרכיב אחר שאינו ויזואלי. טעות נפוצה היא קריאת מדדי התצוגה מהקשר האפליקציה. הערכים שיוחזרו לא יותאמו למדדים של האזור הגלוי שבו מוצגת הפעילות.
- יכול להיות שפעילות מסוימת לא תטפל בשינוי גודל ותקרוס, תציג ממשק משתמש מעוות או תאבד את המצב שלה בגלל הפעלה מחדש בלי לשמור את מצב המופע.
- אפליקציה עשויה לנסות להשתמש בקואורדינטות קלט מוחלטות (במקום בקואורדינטות יחסיות למיקום החלון), מה שעלול לשבש את הקלט במצב ריבוי חלונות.
ב-Android 7 (ובגרסאות מתקדמות יותר), אפשר להגדיר אפליקציהresizeableActivity=false כך שתמיד תפעל במצב מסך מלא. במקרה הזה, הפלטפורמה מונעת פעילויות שלא ניתן לשנות את הגודל שלהן מלהיכנס למסך מפוצל. אם המשתמש מנסה להפעיל פעילות שלא ניתן לשנות את גודלה ממרכז האפליקציות כשהוא כבר במצב מסך מפוצל, הפלטפורמה יוצאת ממצב מסך מפוצל ומפעילה את הפעילות שלא ניתן לשנות את גודלה במצב מסך מלא.
אסור להפעיל במצב ריבוי חלונות אפליקציות שבהן המאפיין הזה מוגדר במניפסט לערך false, אלא אם מופעל מצב התאימות:
- אותה הגדרה חלה על התהליך, שמכיל את כל הפעילויות ואת הרכיבים שאינם פעילויות.
- ההגדרה שהוחלה עומדת בדרישות ה-CDD לגבי מסכים שתואמים לאפליקציות.
ב-Android 10, הפלטפורמה עדיין מונעת פעילויות שלא ניתן לשנות את הגודל שלהן לעבור למצב מסך מפוצל, אבל אפשר לשנות את הגודל שלהן באופן זמני אם הפעילות הצהירה על כיוון קבוע או על יחס גובה-רוחב קבוע. אם לא, גודל הפעילות ישתנה כדי למלא את המסך כולו כמו ב-Android 9 ומטה.
ההטמעה שמוגדרת כברירת מחדל מחילה את המדיניות הבאה:
אם Activity מוצהרת כלא תואמת לריבוי חלונות באמצעות המאפיין android:resizeableActivity, ואם ה-Activity עומדת באחד מהתנאים שמתוארים בהמשך, אז כשצריך לשנות את הגדרת התצורה של המסך שחלה על ה-Activity, ה-Activity והתהליך נשמרים עם ההגדרה המקורית, והמשתמש מקבל מזמינוּת להפעיל מחדש את תהליך האפליקציה כדי להשתמש בהגדרת התצורה של המסך המעודכנת.
- האם הכיוון קבוע באמצעות החלת
android:screenOrientation - האפליקציה כוללת יחס גובה-רוחב מקסימלי או מינימלי שמוגדר כברירת מחדל באמצעות טירגוט לרמת API, או שהיא כוללת הצהרה מפורשת על יחס הגובה-רוחב
באיור הזה מוצגת פעילות שלא ניתן לשנות את הגודל שלה, עם יחס גובה-רוחב מוצהר. כשמקפלים את המכשיר, גודל החלון מוקטן כדי להתאים לאזור, תוך שמירה על יחס הגובה-רוחב באמצעות הוספת מסגרת מתאימה. בנוסף, המשתמש מקבל אפשרות להפעיל מחדש את הפעילות בכל פעם שמשתנה אזור התצוגה של הפעילות.
כשפותחים את המכשיר, ההגדרה, הגודל ויחס הגובה-רוחב של הפעילות לא משתנים, אבל מוצגת האפשרות להפעיל מחדש את הפעילות.
אם המדיניות resizeableActivity לא מוגדרת (או שהיא מוגדרת כ-true), האפליקציה תומכת באופן מלא בשינוי גודל.
הטמעה
פעילות שלא ניתן לשנות את הגודל שלה עם כיוון או יחס גובה-רוחב קבועים נקראת בקוד מצב תאימות לגודל (SCM). המצב מוגדר ב-ActivityRecord#shouldUseSizeCompatMode(). כשמפעילים פעילות SCM, ההגדרה שקשורה למסך (כמו גודל או צפיפות) קבועה בהגדרת הביטול שנדרשת, כך שהפעילות כבר לא תלויה בהגדרת התצוגה הנוכחית.
אם פעילות ה-SCM לא יכולה למלא את המסך כולו, היא מיושרת לחלק העליון וממורכזת אופקית. הגבולות של הפעילות מחושבים על ידי AppWindowToken#calculateCompatBoundsTransformation().
כשפעילות SCM משתמשת בהגדרת תצורה של מסך שונה מהקונטיינר שלה (לדוגמה, אם גודל התצוגה משתנה או אם הפעילות מועברת לתצוגה אחרת), הערך של ActivityRecord#inSizeCompatMode() הוא true והערך של SizeCompatModeActivityController (בממשק המשתמש של המערכת) מקבל את הקריאה החוזרת להצגת הכפתור להפעלה מחדש של התהליך.
גודלי תצוגה ויחסי גובה-רוחב
Android מגרסה 10 ואילך תומך ביחסי גובה-רוחב חדשים, החל מיחסים גבוהים של מסכים ארוכים וצרים ועד ליחסים של 1:1. אפליקציות יכולות להגדיר ApplicationInfo#maxAspectRatio וApplicationInfo#minAspectRatio של המסך שהן יכולות לטפל בהם.

איור 1. דוגמאות ליחסי גובה-רוחב של אפליקציות שנתמכים ב-Android 10
במכשירים יכולים להיות מסכים משניים עם גדלים ורזולוציות קטנים יותר מאלה שנדרשים ב-Android 9 ומגרסאות קודמות (רוחב או גובה מינימלי של 2.5 אינץ', מינימום של 320 נקודות לאינץ' ל-smallestScreenWidth), אבל רק פעילויות שמוגדרות לתמיכה במסכים הקטנים האלה יכולות להיות מוצגות בהם.
אפליקציות יכולות להצטרף על ידי הצהרה על גודל מינימלי נתמך שקטן מגודל התצוגה המיועד או שווה לו. כדי לעשות זאת, משתמשים במאפייני פריסת הפעילות android:minHeight ו-android:minWidth בקובץ AndroidManifest.
מדיניות בנושא מודעות לרשת המדיה
ב-Android 10, מדיניות מסוימת לגבי תצוגה מופרדת ומועברת מההטמעה של ברירת המחדל WindowManagerPolicy ב-PhoneWindowManager למחלקות של תצוגה, כמו:
- מצב התצוגה וסיבוב המסך
- מעקב אחרי חלק מהמקשים ואירועי התנועה
- ממשק המשתמש של המערכת וחלונות קישוט
ב-Android 9 (ובגרסאות קודמות), המחלקה PhoneWindowManager טיפלה במדיניות התצוגה, במצב ובהגדרות, בסיבוב, במעקב אחר מסגרת חלון העיטור ועוד. ב-Android 10, רוב הפונקציות האלה מועברות למחלקה DisplayPolicy, למעט מעקב אחרי סיבוב, שמועבר למחלקה DisplayRotation.
הגדרות של חלון התצוגה
ב-Android 10, הגדרת החלונות שניתנת להגדרה לכל מסך הורחבה וכוללת:
- מצב ברירת המחדל של חלון התצוגה
- ערכי Overscan
- סיבוב משתמשים ומצב סיבוב
- גודל, צפיפות ומצב שינוי קנה מידה שנכפים
- מצב הסרת תוכן (כשהתצוגה מוסרת)
- תמיכה בעיטורים של המערכת וב-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()). אפשר להשתמש בשיטה הזו ב-framework כדי לזהות תצוגות באופן סטטי. לדוגמה, הוא משמש ב-DisplayWindowSettings). DisplayAddress.Network
כולל את כתובת ה-MAC ואפשר ליצור אותו באמצעות DisplayAddress#fromMacAddress().
התוספות האלה מאפשרות ליצרני מכשירים לזהות תצוגות בהגדרות סטטיות של כמה מסכים, ולהגדיר הגדרות ותכונות שונות של המערכת באמצעות מזהי תצוגה סטטיים, כמו יציאות למסכים פיזיים. השיטות האלה מוסתרות ומיועדות לשימוש רק ב-system_server.
בהינתן מזהה תצוגה של HWC (שיכול להיות אטום ולא תמיד יציב), הפונקציה הזו מחזירה את מספר היציאה (ספציפי לפלטפורמה) בן 8 הביטים שמזהה מחבר פיזי לפלט תצוגה, וגם את ה-blob של 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"
שימוש ביותר משני מסכים
ב-Android 9 (ובגרסאות קודמות), SurfaceFlinger ו-DisplayManagerService
הניחו שקיימים לכל היותר שני מסכים פיזיים עם מזהים שמוגדרים בהארדקוד 0 ו-1.
החל מ-Android 10, SurfaceFlinger יכול להשתמש ב-API של Hardware Composer (HWC) כדי ליצור מזהי תצוגה יציבים, וכך לנהל מספר שרירותי של מסכים פיזיים. מידע נוסף מופיע במאמר בנושא מזהים סטטיים של מודעות לרשת המדיה.
המסגרת יכולה לחפש את אסימון IBinder לתצוגה פיזית דרך SurfaceControl#getPhysicalDisplayToken אחרי קבלת מזהה התצוגה בן 64 הביטים מ-SurfaceControl#getPhysicalDisplayIds או מאירוע hotplug של DisplayEventReceiver.
ב-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. המזהים מבוטאים באמצעות סוג אפשרות, כאשר הערך null מייצג תצוגה לא חוקית או תצוגה וירטואלית לא חוקית של 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.