אפשר להשתמש ב-Instrument Cluster API (ממשק API של Android) כדי להציג אפליקציות ניווט, כולל מפות Google, במסך משני ברכב, למשל מאחורי ההגה בלוח המחוונים. בדף הזה מוסבר איך ליצור שירות שישלוט במסך המשני וישלב את השירות עם CarService כדי שאפליקציות ניווט יוכלו להציג ממשק משתמש.
הסברים על המונחים
בדף הזה נעשה שימוש במונחים הבאים.
CarManager that enables external apps to launch an activity on
the Instrument Cluster and receive callbacks when the Instrument Cluster is ready to display
activities.android:singleUser. בכל
רגע נתון, מופעלת לכל היותר רק דוגמה אחת של השירות במערכת Android.דרישות מוקדמות
לפני שממשיכים, חשוב לוודא שיש לכם את הרכיבים הבאים:
- סביבת פיתוח ל-Android.כדי להגדיר את סביבת הפיתוח ל-Android, ראו דרישות לבנייה.
- מורידים את קוד המקור של Android.מורידים את הגרסה העדכנית של קוד המקור של Android מהענף pi-car-release (או מגרסה מאוחרת יותר) בכתובת https://android.googlesource.com.
- מערכת מולטימדיה (HU). מכשיר Android עם Android 9 (או גרסה מתקדמת יותר). למכשיר הזה צריך להיות מסך משלו, והוא צריך להיות מסוגל להציג גרסאות חדשות של Android.
- Instrument Cluster הוא אחד מהבאים:
- מסך משני פיזי שמחובר למערכת מולטימדיה (HU). אם החומרה והליבה (kernel) של המכשיר תומכות בניהול של כמה מסכים.
- יחידה עצמאית. כל יחידה חישובית שמחוברת ל-HU באמצעות חיבור לרשת, שיכולה לקבל ולהציג זרם וידאו במסך שלה.
- תצוגה שעברה אמולציה. במהלך הפיתוח, אפשר להשתמש באחת מסביבות ההדמיה הבאות:
- הדמיה של מסכים משניים. כדי להפעיל תצוגה משנית מדומה בכל הפצת Android AOSP, עוברים להגדרות אפשרויות למפתחים באפליקציית המערכת הגדרות ואז בוחרים באפשרות הדמיה של תצוגות משניות. ההגדרה הזו שוות ערך לחיבור של תצוגה משנית פיזית, עם ההגבלה שהתצוגה הזו מוצגת כשכבת-על מעל התצוגה הראשית.
- לוח מחוונים מדומה. האמולטור של Android שכלול ב-AAOS מספק אפשרות להצגת לוח מחוונים עם ClusterRenderingService.
ארכיטקטורת השילוב
רכיבי שילוב
כל שילוב של Instrument Cluster API כולל את שלושת הרכיבים האלה:
CarService- אפליקציות ניווט
- שירות לוח מחוונים OEM

CarService
CarService מתווך בין אפליקציות ניווט לבין הרכב, כדי להבטיח שרק אפליקציית ניווט אחת תהיה פעילה בכל רגע נתון, ורק אפליקציות עם ההרשאה android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL יוכלו לשלוח נתונים לרכב.
CarService מאתחל את כל השירותים שקשורים לרכב ומספק גישה לשירותים האלה באמצעות סדרה של מנהלים. כדי ליצור אינטראקציה עם השירותים, אפליקציות שפועלות ברכב יכולות לגשת למנהלים האלה.
כדי להטמיע לוח מחוונים, יצרני ציוד מקורי לכלי רכב צריכים ליצור הטמעה מותאמת אישית של InstrumentClusterRendererService ולעדכן את ClusterRenderingService.
במהלך תהליך האתחול, כשמעבדים את לוח המחוונים, CarService קורא את מפתח InstrumentClusterRendererService של ClusterRenderingService כדי לאתר הטמעה של InstrumentClusterService. ב-AOSP, הרשומה הזו מצביעה על שירות העיבוד של הטמעת האשכול לדוגמה של 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 על ידי יצרני ציוד מקורי (OEM) צריכות לאתחל את הצג המשני שמשמש להצגת מידע על המסכים בתא הנוסעים של הרכב, ולהעביר את המידע הזה אל CarService באמצעות קריאה לשיטות InstrumentClusterRendererService.setClusterActivityOptions() ו-InstrumentClusterRendererService.setClusterActivityState().
בפונקציה השנייה, שירות Instrument Cluster צריך לספק הטמעה של הממשק ClusterRenderingService שמקבל אירועים של עדכון סטטוס הניווט, שמקודדים כ-eventType ונתוני אירועים שמקודדים בחבילה.
רצף השילוב
בתרשים הבא מוצגת הטמעה של מצב ניווט שמעדכן את התצוגה:
באיור הזה, הצבעים מציינים את הדברים הבאים:
- צהוב.
CarServiceו-CarNavigationStatusManagerמסופקים על ידי פלטפורמת Android. מידע נוסף זמין במאמרים בנושא רכב ו-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בודק את חבילת APK של אפליקציית הניווט ומאתר פעילות שמסומנת בקטגוריהandroid.car.cluster.NAVIGATION. - אם נמצאה פעילות, אפליקציית הניווט משתמשת ב-
ActivityOptionsשדווח על ידיInstrumentClusterRenderingServiceכדי להפעיל את הפעילות, וכוללת את מאפייני התצוגה של לוח המכוונים כתוספים ב-Intent.
שילוב ה-API
ההטמעה של InstrumentClusterRenderingService צריכה לעמוד בדרישות הבאות:
- צריך להגדיר את השירות כשירות יחיד על ידי הוספת הערך הבא לקובץ AndroidManifest.xml. ההגדרה הזו נדרשת כדי להבטיח שעותק יחיד של שירות לוח המכוונים יפעל, גם במהלך האתחול והחלפת המשתמשים:
android:singleUser="true" - מחזיקים את
BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICEהרשאת המערכת. הדבר הזה מבטיח שרק שירות העיבוד של לוח המכוונים שכלול כחלק מתמונת המערכת של Android יהיה קשור ל-CarService:<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
הטמעה של InstrumentClusterRenderingService
כדי ליצור את השירות:
- כותבים מחלקה שמתרחבת מ-ClusterRenderingService ואז מוסיפים רשומה תואמת לקובץ
AndroidManifest.xml. המחלקות האלה שולטות בתצוגה של לוח המחוונים ויכולות (אופציונלי) לעבד נתונים של Navigation State API. - במהלך
onCreate(), משתמשים בשירות הזה כדי לאתחל את התקשורת עם חומרת העיבוד. האפשרויות כוללות:- קובעים את התצוגה המשנית שתשמש ללוח המכוונים.
- יוצרים תצוגה וירטואלית כדי שאפליקציית לוח המכוונים תבצע רנדרינג ותשדר את התמונה שעברה רנדרינג ליחידה חיצונית (באמצעות פורמט סטרימינג של וידאו, כמו H.264).
- כשהתצוגה שצוינה למעלה מוכנה, השירות הזה צריך לקרוא ל-
InstrumentClusterRenderingService#setClusterActivityLaunchOptions()כדי להגדיר אתActivityOptionsהמדויק שצריך להשתמש בו כדי להציג פעילות בלוח המכוונים. צריך להשתמש בפרמטרים האלה:category.ClusterRenderingService.ActivityOptions.מופעActivityOptionsשאפשר להשתמש בו כדי להפעיל פעילות באשכול הכלים. לדוגמה, מההטמעה של לוח המכוונים לדוגמה ב-AOSP:getService().setClusterActivityLaunchOptions( CATEGORY_NAVIGATION, ActivityOptions.makeBasic() .setLaunchDisplayId(displayId));
- כשאשכול הכלים מוכן להציג פעילויות, השירות הזה צריך להפעיל את
InstrumentClusterRenderingService#setClusterActivityState(). משתמשים בפרמטרים האלה:categoryClusterRenderingService.stateחבילה שנוצרה באמצעות ClusterRenderingService. חשוב לספק את הנתונים הבאים:visibleמציינת שקבוצת המכשירים גלויה ומוכנה להצגת תוכן.unobscuredBoundsמלבן שמגדיר את האזור בתצוגת לוח המכוונים שבו אפשר להציג תוכן. לדוגמה, אזורים שמוצגים בהם חוגות ומדדים.
- אפשר לבטל את השיטה
Service#dump()ולדווח על מידע סטטוס שימושי לניפוי באגים (מידע נוסף זמין במאמר בנושא dumpsys).
דוגמה להטמעה של InstrumentClusterRenderingService
בדוגמה הבאה מוצגת הטמעה של InstrumentClusterRenderingService
שיוצרת VirtualDisplay כדי להציג את התוכן של InstrumentCluster במסך פיזי מרוחק.
לחלופין, הקוד הזה יכול להעביר את displayId של מסך משני פיזי שמחובר למערכת המולטימדיה, אם ידוע שיש מסך כזה.
/** * 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 מספק method בשם getAppTypeOwner(), שמאפשר לשירות האשכול שנכתב על ידי יצרני ציוד מקורי (OEM) לדעת לאיזו אפליקציית ניווט יש התמקדות ברכיב ממשק בכל זמן נתון. יצרני ציוד מקורי יכולים להשתמש בשיטה הקיימת 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 } ...
זיהוי אפליקציות תבניות
באפליקציות ניווט מבוססות-תבניות שמשתמשות בספריית אפליקציות לרכב, הפונקציה CarAppFocusManager#getAppTypeOwner() מחזירה את שם החבילה של המארח (לדוגמה, com.google.android.apps.automotive.templates.host) כי המארח מחזיק במיקוד המערכת בשם אפליקציית הלקוח.
כדי לזהות את אפליקציית הלקוח שדרכה מתבצעת הניווט, יצרני ציוד מקורי יכולים לחלץ את שם החבילה מחבילת מצב הניווט שנשלחת עם CarNavigationStatusManager. שם החבילה מאוחסן במפתח active_app_package_name בחבילה שהתקבלה על ידי NavigationRenderer#onNavigationStateChanged(Bundle):
// In your NavigationRenderer implementation @Override public void onNavigationStateChanged(Bundle bundle) { if (bundle.containsKey("active_app_package_name")) { String activeAppPackage = bundle.getString("active_app_package_name"); // Use the package name to identify the navigating app (e.g., com.waze) } }
נספח: שימוש באפליקציה לדוגמה
AOSP מספק אפליקציה לדוגמה שמטמיעה את Navigation State API.
כדי להריץ את אפליקציית הדוגמה הזו:
- פיתוח והפעלה של Android Auto ביחידת ראש נתמכת. משתמשים בהוראות הבנייה וההפעלה של Android שספציפיות למכשיר שלכם. ההוראות מפורטות במאמר בנושא שימוש בלוחות הפניה.
- מחברים מסך פיזי משני ליחידת הראש (אם יש תמיכה) או מפעילים את יחידת הראש הווירטואלית המשנית:
- בוחרים באפשרות מצב פיתוח באפליקציית ההגדרות.
- עוברים אל הגדרות > מערכת > מתקדם > אפשרויות למפתחים > הדמיה של מסכים משניים.
- הפעלה מחדש של מערכת המולטימדיה (HU)
- כדי להפעיל את אפליקציית KitchenSink:
- פותחים את חלונית ההזזה.
- עוברים אל Inst. Cluster.
- לוחצים על התחלת מטא-נתונים.
הבקשה של KitchenSink היא להתמקד בניווט, ולכן שירות DirectRenderingCluster מציג ממשק משתמש מדומה בלוח המכוונים.