תמיכה בקישוטי המערכת

בהמשך מפורטים העדכונים שבוצעו בתחומים הספציפיים האלה של המסכים:

קישוטי מערכת

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

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

הטמעה

המשתנה DisplayWindowSettings#setShouldShowSystemDecorsLocked() מוצג גם ב-WindowManager#setShouldShowSystemDecors() לצורך בדיקה. הפעלת השיטה הזו במטרה להפעיל עיטורים של מערכת לא מוסיפה חלונות עיטורים שלא היו קיימים קודם, או מסירה אותם אם הם היו קיימים קודם. ברוב המקרים, השינוי בתמיכה בסמלי המערכת נכנס לתוקף רק אחרי הפעלה מחדש של המכשיר.

בדרך כלל, בדיקות לתמיכה בקישוט המערכת בבסיס הקוד של WindowManager עוברות דרך DisplayContent#supportsSystemDecorations(), ואילו בדיקות לשירותים חיצוניים (כמו System UI כדי לבדוק אם יש להציג את סרגל הניווט) עוברות דרך WindowManager#shouldShowSystemDecors(). כדי להבין מה נשלט על ידי ההגדרה הזו, כדאי לבדוק את נקודות הקריאה של השיטות האלה.

חלונות עיצוב לממשק המשתמש של המערכת

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

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

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

הטמעה

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

רכיב של ממשק המשתמש במערכת שתומך בתצוגה מרובת מסכים (MD) צריך לטפל במקרים הבאים:

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

כשממשק המשתמש של המערכת מזהה הוספה של מסך לפני WindowManager, נוצר מצב של תחרות. כדי למנוע את הבעיה הזו, אפשר להטמיע קריאה חוזרת בהתאמה אישית מ-WindowManager ל-System UI כשמוסיפים מסך, במקום להירשם לאירועים של DisplayManager.DisplayListener. להטמעה לדוגמה, תוכלו לעיין במאמר CommandQueue.Callbacks#onDisplayReady לגבי תמיכה בסרגל הניווט ובמאמר WallpaperManagerInternal#onDisplayReady לגבי טפטים.

בנוסף, ב-Android 10 קיימים העדכונים הבאים:

  • הכיתה NavigationBarController קובעת את כל הפונקציונליות הספציפית לסרגלי הניווט.
  • מידע נוסף על סרגל ניווט בהתאמה אישית זמין במאמר CarStatusBar.
  • TYPE_NAVIGATION_BAR כבר לא מוגבל למכונה אחת, ואפשר להשתמש בו בכל מסך.
  • IWindowManager#hasNavigationBar() מתעדכן כך שיכלול את הפרמטר displayId לממשק המשתמש של המערכת בלבד.

מרכז האפליקציות

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

איור 1. דוגמה למרכז אפליקציות עם תצוגה מרובת מסכים עבור platform/development/samples/MultiDisplay

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

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

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

הטמעה

ב-Android 10, RootActivityContainer#startHomeOnDisplay() בוחר באופן אוטומטי את הרכיב והכוונה הרצויים, בהתאם למסך שבו מסך הבית הופעל. RootActivityContainer#resolveSecondaryHomeActivity() מכיל את הלוגיקה לחיפוש רכיב הפעילות במרכז האפליקציות בהתאם למרכז האפליקציות שנבחר, ויכול להשתמש בברירת המחדל של המערכת, במקרה הצורך (ראו ActivityTaskManagerService#getSecondaryHomeIntent()).

הגבלות אבטחה

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

טפטים

ב-Android 10 ואילך), טפטים נתמכים במסכים משניים:

איור 2. טפט דינמי במסכים פנימיים (למעלה) ובמסכים חיצוניים (למטה)

מפתחים יכולים להצהיר על תמיכה בתכונה 'טפט' על ידי ציון הערך android:supportsMultipleDisplays="true" בהגדרת ה-XML של WallpaperInfo. מפתחי טפטים גם צפויים לטעון נכסים באמצעות הקשר התצוגה ב-WallpaperService.Engine#getDisplayContext().

המסגרת יוצרת מופע אחד של WallpaperService.Engine לכל מסך, כך שלכל מנוע יש פלטפורמה והקשר תצוגה משלו. המפתח צריך לוודא שכל מנוע יכול לצייר בנפרד, בקצב פריימים שונה, תוך שמירה על VSYNC.

בחירת טפטים למסכים נפרדים

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

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

  1. משתמשים ב-API WallpaperManager כדי להגדיר את הטפטים.
  2. WallpaperManager מתקבל מאובייקט Context, ולכל אובייקט Context יש מידע על תצוגה תואמת (Context#getDisplay()/getDisplayId()). לכן אפשר לקבל displayId ממכונה WallpaperManager בלי להוסיף methods חדשות.
  3. בצד של ה-framework, צריך להשתמש ב-displayId שהתקבל מאובייקט Context ולמפות אותו למזהה סטטי (כמו יציאה של מסך פיזי). משתמשים במזהה הסטטי כדי לשמור את הטפט שנבחר.

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

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

הגבלות אבטחה

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

הטמעה

ב-Android 10, הממשקים IWallpaperConnection#attachEngine() ו-IWallpaperService#attach() מקבלים את הפרמטר displayId כדי ליצור חיבורים לכל מסך. WallpaperManagerService.DisplayConnector מכיל בתוך עצמו חיבור ומנוע של טפט לכל מסך. ב-WindowManager, בזמן היצירה נוצרים בקרי טפט לכל אובייקט DisplayContent, במקום WallpaperController יחיד לכל המסכים.

חלק מההטמעות הציבוריות של שיטת WallpaperManager (כמו WallpaperManager#getDesiredMinimumWidth()) עודכנו כדי לחשב ולספק מידע למסכים התואמים. נוספו המאפיין WallpaperInfo#supportsMultipleDisplays() ומאפיין משאב תואם, כדי שמפתחי אפליקציות יוכלו לדווח על טפטים שמתאימים למספר מסכים.

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

איור 3. לוגיקה חלופית של טפט למסכים משניים