אפשר להשתמש ב-Instrument Cluster API (ממשק API ל-Android) כדי להציג אפליקציות ניווט, כולל מפות Google, במסך משני ברכב, למשל מאחורי ההגה בלוח המחוונים. בדף הזה נסביר איך ליצור שירות לניהול המסך המשני, ואיך לשלב את השירות עם CarService
כדי שאפליקציות ניווט יוכלו להציג ממשק משתמש.
טרמינולוגיה
בדף הזה נעשה שימוש במונחים הבאים.
CarManager
שמאפשרת לאפליקציות חיצוניות להפעיל פעילות באשכול הכלים ולקבל קריאות חזרה כשאשכול הכלים מוכן להציג פעילויות.android:singleUser
. בכל זמן נתון, רק מופע אחד של השירות פועל במערכת Android.דרישות מוקדמות
לפני שממשיכים, חשוב לוודא שיש לכם את הפריטים הבאים:
- סביבת פיתוח ל-Android במאמר דרישות ה-build מוסבר איך מגדירים את סביבת הפיתוח של Android.
- מורידים את קוד המקור של Android. מורידים את הגרסה האחרונה של קוד המקור של Android מההסתעפות pi-car-release (או מאוחר יותר) בכתובת https://android.googlesource.com.
- יחידה ראשית (HU). מכשיר Android שיכול להריץ את Android 9 (או גרסה מתקדמת יותר). למכשיר הזה צריך להיות מסך משלו, והוא צריך לאפשר לכם להריץ בו גרסאות build חדשות של Android.
- Instrument Cluster הוא אחד מהבאים:
- מסך משני פיזי שמחובר למסך הראשי. אם החומרה והליבה של המכשיר תומכים בניהול של כמה מסכים.
- יחידה עצמאית. כל יחידה מחשוב שמחוברת ל-HU דרך חיבור רשת, עם יכולת לקבל ולציג שידור וידאו במסך משלה.
- תצוגה ממולאלת במהלך הפיתוח, אפשר להשתמש באחת מסביבות ההדמיה הבאות:
- הדמיה של מסכים משניים כדי להפעיל תצוגה משנית מדומה בכל הפצות Android של AOSP, עוברים להגדרות של אפשרויות למפתחים באפליקציית המערכת הגדרות ובוחרים באפשרות הדמיה של תצוגות משניות. ההגדרה הזו זהה לחיבור של תצוגה משנית פיזית, עם המגבלה שהתצוגה הזו חופפת לתצוגה הראשית.
- אשכול כלים בגרסת אמולטור במהדורת Android Emulator שכלולה ב-AAOS יש אפשרות להציג אשכול מכשירים באמצעות ClusterRenderingService.
ארכיטקטורת השילוב
רכיבי השילוב
כל שילוב של Instrument Cluster API מורכב משלושת הרכיבים הבאים:
CarService
- אפליקציות ניווט
- שירות אשכול הכלים של יצרן ציוד מקורי
CarService
CarService
מתווך בין אפליקציות הניווט לבין הרכב, ומוודא שרק אפליקציית ניווט אחת פעילה בכל זמן נתון, ושרק אפליקציות עם ההרשאה android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL
יכולות לשלוח נתונים לרכב.
CarService
מפעיל את כל השירותים הספציפיים לרכב ומספק גישה לשירותים האלה באמצעות סדרה של מנהלי שירותים. כדי ליצור אינטראקציה עם השירותים, לאפליקציות שפועלות ברכב יש גישה למנהלים האלה.
כדי להטמיע אשכול מכשירים, יצרני ציוד מקורי לכלי רכב צריכים ליצור הטמעה בהתאמה אישית של InstrumentClusterRendererService ולעדכן את ClusterRenderingService.
כשמפעילים עיבוד של אשכול כלים, במהלך תהליך האתחול ה-CarService
קורא את המפתח InstrumentClusterRendererService
של ClusterRenderingService כדי לאתר הטמעה של InstrumentClusterService
. ב-AOSP, הרשומה הזו מפנה לשירות ה-render של ההטמעה לדוגמה של Navigation State API באשכול:
<string name="instrumentClusterRendererService"> android.car.cluster/.ClusterRenderingService </string>
השירות שמצוין ברשומה הזו מופעל ומקושר ל-CarService
. כשאפליקציות ניווט, כמו מפות Google, מבקשות CarInstrumentClusterManager
, CarService
מספק מנהל שמעדכן את המצב של לוח המחוונים מה-InstrumentClusterRenderingService
המקושר.
(במקרה הזה, bound מתייחס ל-Android Services).
שירות Instrument Cluster
יצרני ציוד מקורי (OEM) חייבים ליצור חבילת Android (APK) שמכילה קבוצת משנה של ClusterRenderingService.
לכיתה הזו יש שתי מטרות:
- מספק ממשק בין Android לבין מכשיר הרינדור של אשכול המכשירים (המטרה של הדף הזה).
- קבלה ורינדור של עדכונים לגבי מצב הניווט, כמו הנחיות מפורטות לניווט.
למטרה הראשונה, הטמעות של InstrumentClusterRendererService
על ידי יצרני ציוד מקורי חייבות לאתחל את המסך המשני שמשמש לעיבוד מידע במסכים בתא הנוסעים של הרכב, ולשלוח את המידע הזה ל-CarService
באמצעות קריאה לשיטות InstrumentClusterRendererService.setClusterActivityOptions()
ו-InstrumentClusterRendererService.setClusterActivityState()
.
בפונקציה השנייה, שירות Instrument Cluster צריך לספק הטמעה של הממשק ClusterRenderingService שמקבל אירועים של עדכון סטטוס הניווט, שמקודדים כ-eventType
ונתוני אירועים שמקודדים בחבילה.
רצף השילוב
בתרשים הבא מודגמת ההטמעה של מצב ניווט שמציג עדכונים:
באיור הזה, הצבעים מציינים את הדברים הבאים:
- צהוב.
CarService
ו-CarNavigationStatusManager
שסופקו על ידי פלטפורמת Android. מידע נוסף זמין במאמרים Car ו-CAR_NAVIGATION_SERVICE. - ציאן
InstrumentClusterRendererService
שהוטמעה על ידי יצרן הציוד המקורי. - סגול אפליקציית הניווט ש-Google ומפתחים של צד שלישי מטמיעים.
- ירוק.
CarAppFocusManager
. מידע נוסף זמין בקטע שימוש ב-CarAppFocusManager API בהמשך ובמאמר CarAppFocusManager.
תהליך העברת המידע של מצב הניווט מתבצע לפי הסדר הבא:
CarService
מאתחלה אתInstrumentClusterRenderingService
.- במהלך האתחול,
InstrumentClusterRenderingService
מעדכן אתCarService
באמצעות:- מאפייני תצוגה של מרכז המכשירים, כמו גבולות לא מוסתרים (פרטים נוספים על גבולות לא מוסתרים מופיעים בהמשך).
- אפשרויות הפעילות הנדרשות להפעלת פעילויות במסך של אשכול הכלים. מידע נוסף זמין במאמר ActivityOptions.
- אפליקציית ניווט (כמו מפות Google ל-Android Automotive או כל אפליקציית מפות עם ההרשאות הנדרשות):
- הצגת
CarAppFocusManager
באמצעות הכיתה Car מ-car-lib. - לפני תחילת המסלול המפורט, מתבצעות קריאות ל-
CarAppFocusManager.requestFocus()
כדי להעביר את הערךCarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
כפרמטרappType
.
- הצגת
CarAppFocusManager
מעביר את הבקשה הזו אלCarService
. אם ההרשאה תאושר,CarService
יבדוק את חבילת אפליקציית הניווט ויאתר פעילות שמסומנת בקטגוריהandroid.car.cluster.NAVIGATION
.- אם הוא נמצא, אפליקציית הניווט משתמשת ב-
ActivityOptions
שדווח על ידיInstrumentClusterRenderingService
כדי להפעיל את הפעילות, ומצרפת את מאפייני התצוגה של אשכול הכלים כפרטים נוספים בכוונה.
שילוב ה-API
ההטמעה של InstrumentClusterRenderingService
חייבת:
- מוסיפים את הערך הבא לקובץ AndroidManifest.xml כדי להגדיר את השירות כשירות יחיד (singleton). הפעולה הזו נחוצה כדי לוודא שעותק יחיד של שירות Instrument Cluster פועל, גם במהלך האתחול והמעבר בין משתמשים:
android:singleUser="true"
- לוחצים לחיצה ארוכה על הרשאת המערכת
BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE
. כך מובטח שרק שירות הרינדור של Instrument Cluster שכלול בתמונת מערכת Android יהיה מקושר ל-CarService
:<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
הטמעת InstrumentClusterRenderingService
כדי ליצור את השירות:
- כותבים כיתה שמתרחבת מ-ClusterRenderingService ומוסיפים רשומה תואמת לקובץ
AndroidManifest.xml
. הכיתה הזו קובעת את התצוגה של אשכול הכלים, ויכולה (אופציונלי) להציג נתוני API של מצב הניווט. - במהלך
onCreate()
, משתמשים בשירות הזה כדי לאתחל את התקשורת עם חומרת הרינדור. האפשרויות כוללות:- קובעים איזה מסך משני ישמש את אשכול המכשירים.
- יוצרים מסך וירטואלי כדי שהאפליקציה Instrument Cluster תעבד ותשלח את התמונה שעברה עיבוד ליחידת חיצונית (באמצעות פורמט סטרימינג של וידאו, כמו H.264).
- כשהתצוגה שצוינה למעלה מוכנה, השירות הזה צריך להפעיל את
InstrumentClusterRenderingService#setClusterActivityLaunchOptions()
כדי להגדיר את הערך המדויק שלActivityOptions
שצריך להשתמש בו כדי להציג פעילות באשכול המכשירים. משתמשים בפרמטרים הבאים:category.
ClusterRenderingService.ActivityOptions.
מכונה שלActivityOptions
שאפשר להשתמש בה כדי להפעיל פעילות באשכול הכלים. לדוגמה, מהטמעה לדוגמה של Instrument Cluster ב-AOSP:getService().setClusterActivityLaunchOptions( CATEGORY_NAVIGATION, ActivityOptions.makeBasic() .setLaunchDisplayId(displayId));
- כשאשכול הכלים מוכן להציג פעילויות, השירות הזה צריך להפעיל את
InstrumentClusterRenderingService#setClusterActivityState()
. משתמשים בפרמטרים הבאים:category
ClusterRenderingService.state
חבילה שנוצרה באמצעות ClusterRenderingService. חשוב לספק את הנתונים הבאים:visible
מציין שהמרכז למדידת מדדים גלוי ומוכן להציג תוכן.unobscuredBounds
מלבן שמגדיר את האזור במסך של מרכז המכשירים שבו אפשר להציג תוכן בבטחה. לדוגמה, אזורים שמכוסים במדדים ובשעונים.
- משנים את השיטה
Service#dump()
ומדווחים על נתוני סטטוס שיעזרו בניפוי באגים (מידע נוסף זמין במאמר dumpsys).
דוגמה להטמעה של InstrumentClusterRenderingService
בדוגמה הבאה מפורטת הטמעה של InstrumentClusterRenderingService
, שבה נוצר VirtualDisplay
כדי להציג את התוכן של Instrument Cluster במסך פיזי מרוחק.
לחלופין, הקוד הזה יכול להעביר את displayId
של מסך משני פיזי שמחובר ל-HU, אם ידוע שיש כזה.
/** * Sample {@link InstrumentClusterRenderingService} implementation */ public class SampleClusterServiceImpl extends InstrumentClusterRenderingService { // Used to retrieve or create displays private final DisplayManager mDisplayManager; // Unique identifier for the display to be used for instrument // cluster private final String mUniqueId = UUID.randomUUID().toString(); // Format of the instrument cluster display private static final int DISPLAY_WIDTH = 1280; private static final int DISPLAY_HEIGHT = 720; private static final int DISPLAY_DPI = 320; // Area not covered by instruments private static final int DISPLAY_UNOBSCURED_LEFT = 40; private static final int DISPLAY_UNOBSCURED_TOP = 0; private static final int DISPLAY_UNOBSCURED_RIGHT = 1200; private static final int DISPLAY_UNOBSCURED_BOTTOM = 680; @Override public void onCreate() { super.onCreate(); // Create a virtual display to render instrument cluster activities on mDisplayManager = getSystemService(DisplayManager.class); VirtualDisplay display = mDisplayManager.createVirtualDisplay( mUniqueId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_DPI, null, 0 /* flags */, null, null); // Do any additional initialization (e.g.: start a video stream // based on this virtual display to present activities on a remote // display). onDisplayReady(display.getDisplay()); } private void onDisplayReady(Display display) { // Report activity options that should be used to launch activities on // the instrument cluster. String category = CarInstrumentClusterManager.CATEGORY_NAVIGATION; ActionOptions options = ActivityOptions.makeBasic() .setLaunchDisplayId(display.getDisplayId()); setClusterActivityOptions(category, options); // Report instrument cluster state. Rect unobscuredBounds = new Rect(DISPLAY_UNOBSCURED_LEFT, DISPLAY_UNOBSCURED_TOP, DISPLAY_UNOBSCURED_RIGHT, DISPLAY_UNOBSCURED_BOTTOM); boolean visible = true; ClusterActivityState state = ClusterActivityState.create(visible, unobscuredBounds); setClusterActivityState(category, options); } }
שימוש ב-CarAppFocusManager API
ב-CarAppFocusManager API יש שיטה בשם getAppTypeOwner()
, שמאפשרת לשירות האשכולות שנכתב על ידי יצרני ציוד מקורי לדעת לאיזו אפליקציית ניווט יש עדיפות ניווט בכל זמן נתון. יצרני ציוד מקורי יכולים להשתמש בשיטה הקיימת CarAppFocusManager#addFocusListener()
, ואז להשתמש ב-getAppTypeOwner()
כדי לדעת איזו אפליקציה נמצאת במוקד. בעזרת המידע הזה, יצרני ציוד מקורי יכולים:
- החלפת הפעילות שמוצגת באשכול לפעילות באשכול שמספקת אפליקציית הניווט שבה מופעל המיקוד.
- יכולה לזהות אם יש פעילות באשכול באפליקציית הניווט שבה מתמקדים. אם לאפליקציית הניווט שבה מתמקדים אין פעילות באשכול (או אם הפעילות הזו מושבתת), יצרני ציוד מקורי יכולים לשלוח את האות הזה ל-DIM ברכב כדי לדלג על היבט הניווט באשכול לגמרי.
משתמשים ב-CarAppFocusManager
כדי להגדיר את מוקד האפליקציה הנוכחי, כמו ניווט פעיל או פקודה קולית, ולהקשיב לו. בדרך כלל רק מופע אחד של אפליקציה כזו פועל באופן פעיל (או ממוקד) במערכת.
משתמשים בשיטה CarAppFocusManager#addFocusListener(..)
כדי להאזין לשינויים במיקוד של האפליקציה:
import android.car.CarAppFocusManager; ... Car car = Car.createCar(this); mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE); mAppFocusManager.addFocusListener(this, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); ... public void onAppFocusChanged(int appType, boolean active) { // Use the CarAppFocusManager#getAppTypeOwner(appType) method call // to retrieve a list of active package names }
משתמשים בשיטה CarAppFocusManager#getAppTypeOwner(..)
כדי לאחזר את שמות החבילות של הבעלים הנוכחי של סוג אפליקציה נתון שנמצא במוקד. אם הבעלים הנוכחי משתמש בתכונה android:sharedUserId
, יכול להיות שהשיטה הזו תחזיר יותר משם חבילה אחד.
import android.car.CarAppFocusManager; ... Car car = Car.createCar(this); mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE); List<String> focusOwnerPackageNames = mAppFocusManager.getAppTypeOwner( CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); if (focusOwnerPackageNames == null || focusOwnerPackageNames.isEmpty()) { // No Navigation app has focus // OEM may choose to show their default cluster view } else { // focusOwnerPackageNames // Use the PackageManager to retrieve the cluster activity for the package(s) // returned in focusOwnerPackageNames } ...
נספח: שימוש באפליקציה לדוגמה
ב-AOSP יש אפליקציה לדוגמה שמטמיעה את Navigation State API.
כדי להריץ את האפליקציה לדוגמה הזו:
- פיתוח וטעינה של Android Auto ב-HU נתמך. משתמשים בהוראות הספציפיות למכשיר שלכם ליצירה ולעדכון של Android. להוראות, ראו שימוש בלוחות עזר.
- מחברים מסך משני פיזי ל-HU (אם יש תמיכה) או מפעילים את המסך המשני הווירטואלי של ה-HU:
- בוחרים באפשרות מצב פיתוח באפליקציית ההגדרות.
- עוברים אל הגדרות > מערכת > מתקדם > אפשרויות למפתחים > סימולציה של מסכים משניים.
- מפעילים מחדש את HU
- כדי להפעיל את אפליקציית KitchenSink:
- פותחים את חלונית ההזזה.
- עוברים אל Inst. Cluster.
- לוחצים על התחלת המטא-נתונים.
KitchenSink מבקש להתמקד ב-NAVIGATION, שמורה לשירות DirectRenderingCluster
להציג ממשק משתמש מדומה באשכול המכשירים.