שימוש בספריית האפליקציות של Android למכוניות

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

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

לפני שמתחילים

  1. כדאי לעיין בדפים של עיצוב לנהיגה שעוסקים בספריית האפליקציות לרכב.
  2. כדאי לעיין במונחים והמושגים המרכזיים בקטע הבא.
  3. כדאי להכיר את ממשק המשתמש של מערכת Android Auto ואת העיצוב של Android Automotive OS.
  4. כדאי לעיין בנתוני הגרסה.
  5. כדאי לעיין בטעימות.

מונחים ומושגים מרכזיים

מודלים ותבניות
ממשק המשתמש מיוצג על ידי גרף של אובייקטים של מודל שאפשר לארגן יחד בדרכים שונות, בהתאם לשימוש בתבנית שאליה הם שייכים. תבניות הן קבוצת משנה של המודלים שיכולים לשמש כשורש בתרשים. מודלים כוללים את המידע שיוצג למשתמש בצורה של טקסט ותמונות, וגם מאפיינים כדי להגדיר היבטים של המראה החזותי של המידע הזה — לדוגמה, צבעי טקסט או גדלים של תמונות. המארח ממיר את המודלים לתצוגות שנועדו לעמוד בסטנדרטים של הסחות דעת של הנהג, ומטפל בפרטים כמו מגוון הגורמים של המסך ברכב ותבניות הקלט.
מארח
המארח הוא רכיב הקצה העורפי שמטמיע את הפונקציונליות שמציעים ממשקי ה-API של הספרייה, כדי שהאפליקציה תוכל לפעול ברכב. המארח אחראי על גילוי האפליקציה וניהול מחזור החיים שלה, על המרת המודלים לתצוגות ועל שליחת התראות לאפליקציה על אינטראקציות של משתמשים. במכשירים ניידים, המארח הזה מיושם על ידי Android Auto. ב-Android Automotive OS, המארח הזה מותקן כאפליקציית מערכת.
הגבלות על תבניות
תבניות שונות אוכפות הגבלות על התוכן של המודלים שלהן. לדוגמה, בתבניות של רשימות יש מגבלות על מספר הפריטים שאפשר להציג למשתמש. יש גם הגבלות על האופן שבו אפשר לקשר תבניות כדי ליצור את תהליך העבודה של המשימה. לדוגמה, האפליקציה יכולה לדחוף לסטאק המסכים עד חמש תבניות. פרטים נוספים זמינים במאמר הגבלות על תבניות.
Screen
Screen היא מחלקה שמסופקת על ידי הספרייה שאפליקציות מטמיעות כדי לנהל את ממשק המשתמש שמוצג למשתמש. ל-Screen יש מחזור חיים והוא מספק את המנגנון שבו האפליקציה תשלח את התבנית כך שתוצג במסך. אפשר גם לדחוף ולשלוף מכונות Screen אל מקבץ Screen וממנו, כדי לוודא שהן עומדות בהגבלות על תהליך היצירה של התבניות.
CarAppService
CarAppService היא כיתה מופשטת מסוג Service שצריך להטמיע ולייצא מהאפליקציה כדי שהמארח יוכל לזהות ולנהל אותה. באחריות CarAppService של האפליקציה שלכם לאמת שהחיבור של המארח יכול להיות מהימן באמצעות createHostValidator, ולאחר מכן לספק מכונות של Session לכל חיבור באמצעות onCreateSession.
Session

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

כשמפעילים את Session, למשל כשהאפליקציה מופעלת בפעם הראשונה, המארח מבקש להציג את ה-Screen הראשוני באמצעות המתודה onCreateScreen.

התקנת ספריית האפליקציות לרכב

הוראות להוספת הספרייה לאפליקציה מפורטות בדף הגרסה של ספריית Jetpack.

הגדרת קובצי המניפסט של האפליקציה

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

הצהרה על CarAppService

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

צריך גם להצהיר על הקטגוריה של האפליקציה ברכיב <category> של מסנן ה-Intent של האפליקציה. הערכים המותרים לאלמנט הזה מפורטים ברשימת הקטגוריות הנתמכות של האפליקציות.

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

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService"/>
        <category android:name="androidx.car.app.category.POI"/>
      </intent-filter>
    </service>

    ...
<application>

קטגוריות של אפליקציות נתמכות

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

במאמר איכות אפליקציות Android לרכבים מפורטים תיאורים מפורטים של כל קטגוריה ושל הקריטריונים לאפליקציות ששייכות אליהן.

ציון השם והסמל של האפליקציה

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

אפשר לציין את השם והסמל של האפליקציה שישמשו לייצוג האפליקציה באמצעות המאפיינים label ו-icon של CarAppService:

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

אם לא מוצהר על התווית או הסמל ברכיב <service>, המארח חוזר לערכים שצוינו ברכיב <application>.

הגדרת עיצוב בהתאמה אישית

כדי להגדיר עיצוב מותאם אישית לאפליקציה ברכב, מוסיפים את הרכיב <meta-data> לקובץ המניפסט באופן הבא:

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

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

<resources>
  <style name="MyCarAppTheme">
    <item name="carColorPrimary">@layout/my_primary_car_color</item>
    <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item>
    <item name="carColorSecondary">@layout/my_secondary_car_color</item>
    <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item>
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

רמת ה-API של האפליקציה לרכב

ספריית האפליקציות של הרכב מגדירה רמות API משלה, כדי שתוכלו לדעת אילו תכונות ספרייה נתמכות על ידי מארח התבניות ברכב. כדי לאחזר את רמת ה-API הגבוהה ביותר של אפליקציה לרכב שנתמכת על ידי מארח, משתמשים ב-method‏ getCarAppApiLevel().

בקובץ AndroidManifest.xml, צריך להצהיר על רמת ה-API המינימלית של אפליקציית Car באפליקציה שלך:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

במסמכי העזרה של ההערה RequiresCarApi מוסבר איך לשמור על תאימות לאחור ולהצהיר על רמת ה-API המינימלית הנדרשת לשימוש בתכונה. כדי לבדוק איזה רמת API נדרשת כדי להשתמש בתכונה מסוימת של ספריית האפליקציות לרכב, אפשר לעיין במסמכי העזרה של CarAppApiLevels.

יצירת CarAppService ו-Session

האפליקציה צריכה להרחיב את הכיתה CarAppService ולהטמיע את השיטה onCreateSession שלה, שמחזירה מופע של Session שתואם לחיבור הנוכחי למארח:

Kotlin

class HelloWorldService : CarAppService() {
    ...
    override fun onCreateSession(): Session {
        return HelloWorldSession()
    }
    ...
}

Java

public final class HelloWorldService extends CarAppService {
    ...
    @Override
    @NonNull
    public Session onCreateSession() {
        return new HelloWorldSession();
    }
    ...
}

המכונה Session אחראית להחזרת המכונה Screen לשימוש בפעם הראשונה שהאפליקציה מופעלת:

Kotlin

class HelloWorldSession : Session() {
    ...
    override fun onCreateScreen(intent: Intent): Screen {
        return HelloWorldScreen(carContext)
    }
    ...
}

Java

public final class HelloWorldSession extends Session {
    ...
    @Override
    @NonNull
    public Screen onCreateScreen(@NonNull Intent intent) {
        return new HelloWorldScreen(getCarContext());
    }
    ...
}

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

יצירת מסך הבית

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

קטע הקוד הבא מראה איך להצהיר על Screen שמשתמש בתבנית PaneTemplate כדי להציג מחרוזת פשוטה Hello world!‎:

Kotlin

class HelloWorldScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val row = Row.Builder().setTitle("Hello world!").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

Java

public class HelloWorldScreen extends Screen {
    @NonNull
    @Override
    public Template onGetTemplate() {
        Row row = new Row.Builder().setTitle("Hello world!").build();
        Pane pane = new Pane.Builder().addRow(row).build();
        return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
    }
}

הכיתה CarContext

הכיתה CarContext היא קבוצת משנה של ContextWrapper שזמינה למכונות Session ו-Screen. הוא מספק גישה לשירותי הרכב, כמו ScreenManager לניהול מקבץ המסכים, AppManager לפונקציונליות כללית שקשורה לאפליקציות, כמו גישה לאובייקט Surface לצורך ציור מפות, ו-NavigationManager שמשמש אפליקציות ניווט מסלול-על-מסלול כדי לשלוח למארח מטא-נתונים של ניווט ואירועים אחרים שקשורים לניווט.

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

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

הטמעת ניווט במסך

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

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

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

Kotlin

val template = MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener { screenManager.push(NextScreen(carContext)) }
            .build())
    .build()

Java

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(
                () -> getScreenManager().push(new NextScreen(getCarContext())))
            .build())
    .build();

האובייקט Action.BACK הוא אובייקט Action סטנדרטי שמפעיל באופן אוטומטי את ScreenManager.pop. אפשר לשנות את ההתנהגות הזו באמצעות המכונה OnBackPressedDispatcher שזמינה ב-CarContext.

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

רענון התוכן של תבנית

האפליקציה יכולה לבקש לבטל את התוקף של תוכן של Screen על ידי קריאה ל-method‏ Screen.invalidate. לאחר מכן, המארח קורא חזרה לשיטה Screen.onGetTemplate של האפליקציה כדי לאחזר את התבנית עם התוכן החדש.

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

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

ציור מפות

אפליקציות ניווט ונקודות עניין (POI) שמשתמשות בתבניות הבאות יכולות לצייר מפות על ידי גישה ל-Surface:

תבנית הרשאת תבנית הנחיות לגבי קטגוריות
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ניווט
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES או
androidx.car.app.MAP_TEMPLATES
ניווט, POI
MapTemplate (הווצא משימוש) androidx.car.app.NAVIGATION_TEMPLATES ניווט
PlaceListNavigationTemplate (הווצא משימוש) androidx.car.app.NAVIGATION_TEMPLATES ניווט
RoutePreviewNavigationTemplate (הווצא משימוש) androidx.car.app.NAVIGATION_TEMPLATES ניווט

הצהרה על הרשאת הפנים

בנוסף להרשאה שנדרשת לתבנית שבה האפליקציה משתמשת, האפליקציה צריכה להצהיר על ההרשאה androidx.car.app.ACCESS_SURFACE בקובץ AndroidManifest.xml שלה כדי לקבל גישה לממשק:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
  ...
</manifest>

גישה לפלטפורמה

כדי לגשת ל-Surface שהמארח מספק, צריך להטמיע SurfaceCallback ולספק את ההטמעה הזו לשירות הרכב AppManager. הערך הנוכחי של הפרמטר Surface מועבר ל-SurfaceCallback בפרמטר SurfaceContainer של הקריאות החוזרות (callback) של onSurfaceAvailable() ו-onSurfaceDestroyed().

Kotlin

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

Java

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

הסבר על האזור הגלוי של המשטח

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

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

תמיכה בעיצוב כהה

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

כדי להחליט אם לצייר מפה כהה, אפשר להשתמש ב-method‏ CarContext.isDarkMode. בכל פעם שסטטוס העיצוב הכהה משתנה, תקבלו שיחה למספר Session.onCarConfigurationChanged.

מאפשרים למשתמשים לבצע פעולות במפה

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

תבנית תמיכה באינטראקטיביות החל מרמת ה-API של אפליקציית הרכב
NavigationTemplate 2
PlaceListNavigationTemplate (הווצא משימוש) 4
RoutePreviewNavigationTemplate (הווצא משימוש) 4
MapTemplate (הווצא משימוש) 5 (introduction of template)
MapWithContentTemplate 7 (introduction of template)

הטמעת קריאות חזרה (callbacks) של אינטראקטיביות

לממשק SurfaceCallback יש כמה שיטות קריאה חוזרת (callback) שאפשר להטמיע כדי להוסיף אינטראקטיביות למפות שנוצרו באמצעות התבניות בקטע הקודם:

אינטראקציה השיטה SurfaceCallback נתמכת החל מרמת API של אפליקציה לרכב
הקשה onClick 5
אפשר לצבוט כדי לשנות את מרחק התצוגה onScale 2
גרירה בנגיעה אחת onScroll 2
החלקה בנגיעה אחת onFling 2
הקשה כפולה onScale (עם גורם קנה מידה שנקבע על ידי מארח התבנית) 2
דחיפה סיבובית במצב הזזה onScroll (עם גורם מרחק שנקבע על ידי מארח התבנית) 2

הוספת סרגל פעולות למפה

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

כדי לקבל קריאות חוזרות (callback) של אינטראקטיביות במפה, צריך להוסיף את הלחצן Action.PAN בשורת הפעולות במפה. כשהמשתמש מקשיב על לחצן ה-Pan, המארח עובר למצב Pan, כפי שמתואר בקטע הבא.

אם האפליקציה שלכם לא כוללת את הלחצן Action.PAN בסרגל הפעולות של המפה, היא לא תקבל קלט מהמשתמשים מהשיטות SurfaceCallback והמארח ייצא מכל מצב החלקה שהופעל בעבר.

במסך מגע, לחצן התנועה לא מוצג.

הסבר על מצב הזזה

במצב 'הזזה', מארח התבנית מתרגם את הקלט של המשתמש ממכשירי קלט ללא מגע, כמו בקרי רוטורים ומשטחי מגע, לשיטות SurfaceCallback המתאימות. מגיבים לפעולה של המשתמש כדי להיכנס או לצאת ממצב ה-pan באמצעות השיטה setPanModeListener ב-NavigationTemplate.Builder. המארח יכול להסתיר רכיבים אחרים של ממשק המשתמש בתבנית בזמן שהמשתמש נמצא במצב הזזה.

אינטראקציה עם המשתמש

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

טיפול בקלט של משתמשים

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

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

לאחר מכן, השיטה onClickNavigate יכולה להפעיל את אפליקציית הניווט שמוגדרת כברירת מחדל ברכב באמצעות השיטה CarContext.startCarApp:

Kotlin

private fun onClickNavigate() {
    val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address))
    carContext.startCarApp(intent)
}

Java

private void onClickNavigate() {
    Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
    getCarContext().startCarApp(intent);
}

לפרטים נוספים על הפעלת אפליקציות, כולל הפורמט של ה-Intent ACTION_NAVIGATE, עיינו בקטע התנעת רכב עם Intent.

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

Kotlin

val row = Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone))
    .build()

Java

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone))
    .build();

הצגת התראות

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

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

Kotlin

val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build()

Java

Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build();

ההתראות יכולות להשפיע על החלקים הבאים בממשק המשתמש:

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

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

אם NotificationCompat.Builder.setOnlyAlertOnce נקרא עם הערך true, התראה בעדיפות גבוהה תוצג כ-HUN רק פעם אחת.

למידע נוסף על עיצוב ההתראות של אפליקציית הרכב, אפשר לעיין במדריך של Google Design for Driving בנושא התראות.

הצגת הודעות קופצות

האפליקציה יכולה להציג הודעה קופצת בעזרת CarToast כמו שמוצג בקטע הקוד הזה:

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

בקשת הרשאות

אם האפליקציה שלכם זקוקה לגישה לנתונים או לפעולות מוגבלים – למשל מיקום – חלים הכללים הרגילים של הרשאות Android. כדי לבקש הרשאה, אפשר להשתמש ב-method CarContext.requestPermissions().

היתרון של השימוש ב-CarContext.requestPermissions(), בהשוואה לשימוש בממשקי API רגילים של Android, הוא שאין צורך להפעיל Activity משלכם כדי ליצור את תיבת הדו-שיח של ההרשאות. בנוסף, אפשר להשתמש באותו קוד גם ב-Android Auto וגם ב-Android Automotive OS, במקום ליצור תהליכים תלויי-פלטפורמה.

עיצוב תיבת הדו-שיח להרשאות ב-Android Auto

ב-Android Auto, תיבת הדו-שיח של ההרשאות למשתמש תופיע בטלפון. כברירת מחדל, לא יופיע רקע מאחורי תיבת הדו-שיח. כדי להגדיר רקע בהתאמה אישית, מגדירים עיצוב של אפליקציה לרכב בקובץ AndroidManifest.xml ומגדירים את המאפיין carPermissionActivityLayout לעיצוב של אפליקציית הרכב.

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

לאחר מכן, מגדירים את המאפיין carPermissionActivityLayout לעיצוב של האפליקציה לרכב:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

הפעלת אפליקציה לרכב באמצעות כוונה (intent)

אפשר לשלוח קריאה ל-method CarContext.startCarApp כדי לבצע אחת מהפעולות הבאות:

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

Kotlin

val notification = notificationBuilder
    ...
    .extend(
        CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(ComponentName(context, MyNotificationReceiver::class.java)),
                    0))
            .build())

Java

Notification notification = notificationBuilder
    ...
    .extend(
        new CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    new Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(new ComponentName(context, MyNotificationReceiver.class)),
                    0))
            .build());

האפליקציה שלכם צריכה גם להצהיר על BroadcastReceiver שמופעל כדי לעבד את הכוונה כשהמשתמש בוחר בפעולה בממשק ההתראות ומפעיל את CarContext.startCarApp עם כוונה שכוללת את ה-URI של הנתונים:

Kotlin

class MyNotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val intentAction = intent.action
        if (ACTION_VIEW_PARKING_RESERVATION == intentAction) {
            CarContext.startCarApp(
                intent,
                Intent(Intent.ACTION_VIEW)
                    .setComponent(ComponentName(context, MyCarAppService::class.java))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)))
        }
    }
}

Java

public class MyNotificationReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction();
        if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
            CarContext.startCarApp(
                intent,
                new Intent(Intent.ACTION_VIEW)
                    .setComponent(new ComponentName(context, MyCarAppService.class))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
        }
    }
}

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

Kotlin

override fun onNewIntent(intent: Intent) {
    val screenManager = carContext.getCarService(ScreenManager::class.java)
    val uri = intent.data
    if (uri != null
        && MY_URI_SCHEME == uri.scheme
        && MY_URI_HOST == uri.schemeSpecificPart
        && ACTION_VIEW_PARKING_RESERVATION == uri.fragment
    ) {
        val top = screenManager.top
        if (top !is ParkingReservationScreen) {
            screenManager.push(ParkingReservationScreen(carContext))
        }
    }
}

Java

@Override
public void onNewIntent(@NonNull Intent intent) {
    ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
    Uri uri = intent.getData();
    if (uri != null
        && MY_URI_SCHEME.equals(uri.getScheme())
        && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
        && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())
    ) {
        Screen top = screenManager.getTop();
        if (!(top instanceof ParkingReservationScreen)) {
            screenManager.push(new ParkingReservationScreen(getCarContext()));
        }
    }
}

בקטע הצגת התראות מוסבר איך לטפל בהתראות באפליקציה לרכב.

הגבלות על תבניות

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

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

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

רענון התבניות

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

פעולות אחורה

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

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

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

פעולות איפוס

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

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

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

Connection API

כדי לקבוע אם האפליקציה פועלת ב-Android Auto או ב-Android Automotive OS, אפשר להשתמש ב-CarConnection API כדי לאחזר את פרטי החיבור בזמן הריצה.

לדוגמה, ב-Session של אפליקציית הרכב, מאתחלים CarConnection ומירשמו לעדכונים של LiveData:

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

לאחר מכן, תוכלו להגיב לשינויים במצב החיבור במעקב:

Kotlin

fun onConnectionStateUpdated(connectionState: Int) {
  val message = when(connectionState) {
    CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit"
    CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS"
    CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto"
    else -> "Unknown car connection type"
  }
  CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show()
}

Java

private void onConnectionStateUpdated(int connectionState) {
  String message;
  switch(connectionState) {
    case CarConnection.CONNECTION_TYPE_NOT_CONNECTED:
      message = "Not connected to a head unit";
      break;
    case CarConnection.CONNECTION_TYPE_NATIVE:
      message = "Connected to Android Automotive OS";
      break;
    case CarConnection.CONNECTION_TYPE_PROJECTION:
      message = "Connected to Android Auto";
      break;
    default:
      message = "Unknown car connection type";
      break;
  }
  CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show();
}

Constraints API

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

קודם כול, מקבלים ConstraintManager מה-CarContext:

Kotlin

val manager = carContext.getCarService(ConstraintManager::class.java)

Java

ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);

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

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);

הוספת תהליך כניסה

אם האפליקציה שלכם מציעה למשתמשים חוויית כניסה לחשבון, תוכלו להשתמש בתבניות כמו SignInTemplate ו-LongMessageTemplate עם Car App API ברמה 2 ואילך כדי לטפל בכניסה לאפליקציה במסך הרכב.

כדי ליצור SignInTemplate, מגדירים SignInMethod. בשלב הזה, בספריית האפליקציות לרכב יש תמיכה בשיטות הכניסה הבאות:

  • InputSignInMethod כדי להיכנס באמצעות שם משתמש וסיסמה.
  • PinSignInMethod להתחברות באמצעות מספר PIN, שבה המשתמש מקשר את החשבון שלו מהטלפון באמצעות מספר PIN שמוצג במכשיר הראשי.
  • ProviderSignInMethod כניסה באמצעות ספק, כמו כניסה באמצעות חשבון Google ו-One Tap.
  • QRCodeSignInMethod כניסה באמצעות קוד QR, שבה המשתמש סורק קוד QR כדי להשלים את הכניסה בטלפון. התכונה הזו זמינה ב-Car API ברמה 4 ואילך.

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

Kotlin

val callback = object : InputCallback {
    override fun onInputSubmitted(text: String) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    override fun onInputTextChanged(text: String) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
}

Java

InputCallback callback = new InputCallback() {
    @Override
    public void onInputSubmitted(@NonNull String text) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    @Override
    public void onInputTextChanged(@NonNull String text) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
};

נדרש InputCallback לInputSignInMethod Builder.

Kotlin

val passwordInput = InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build()

Java

InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build();

לבסוף, משתמשים ב-InputSignInMethod החדש כדי ליצור SignInTemplate.

Kotlin

SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build()

Java

new SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build();

שימוש ב-AccountManager

אפליקציות ל-Android Automotive OS עם אימות חייבות להשתמש ב-AccountManager מהסיבות הבאות:

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

הוספת וריאנטים של מחרוזות טקסט

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

אפשר להוסיף וריאנטים של מחרוזות טקסט ל-CarText באמצעות השיטה CarText.Builder.addVariant():

Kotlin

val itemTitle = CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build()

Java

CarText itemTitle = new CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build();

לאחר מכן תוכלו להשתמש ב-CarText הזה – לדוגמה, כטקסט הראשי של GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

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

הוספת CarIcons בשורה

אפשר להוסיף סמלים מוטבעים עם טקסט כדי להעשיר את מראה האפליקציה באמצעות CarIconSpan. מידע נוסף על יצירת הקטעים האלה זמין במסמכי התיעוד של CarIconSpan.create. במאמר Spantastic text styling with Spans מוסבר איך עובדת הוספת סגנון לטקסט באמצעות Spans.

Kotlin

  
val rating = SpannableString("Rating: 4.5 stars")
rating.setSpan(
    CarIconSpan.create(
        // Create a CarIcon with an image of four and a half stars
        CarIcon.Builder(...).build(),
        // Align the CarIcon to the baseline of the text
        CarIconSpan.ALIGN_BASELINE
    ),
    // The start index of the span (index of the character '4')
    8,
    // The end index of the span (index of the last 's' in "stars")
    16,
    Spanned.SPAN_INCLUSIVE_INCLUSIVE
)

val row = Row.Builder()
    ...
    .addText(rating)
    .build()
  
  

Java

  
SpannableString rating = new SpannableString("Rating: 4.5 stars");
rating.setSpan(
        CarIconSpan.create(
                // Create a CarIcon with an image of four and a half stars
                new CarIcon.Builder(...).build(),
                // Align the CarIcon to the baseline of the text
                CarIconSpan.ALIGN_BASELINE
        ),
        // The start index of the span (index of the character '4')
        8,
        // The end index of the span (index of the last 's' in "stars")
        16,
        Spanned.SPAN_INCLUSIVE_INCLUSIVE
);
Row row = new Row.Builder()
        ...
        .addText(rating)
        .build();
  
  

ממשקי API לחומרת רכב

החל מרמת API 3 של Car App API, בספריית האפליקציות לרכב יש ממשקי API שאפשר להשתמש בהם כדי לגשת לחיישנים ולמאפיינים של הרכב.

הדרישות

כדי להשתמש ב-API עם Android Auto, קודם צריך להוסיף יחסי תלות ל-androidx.car.app:app-projected לקובץ build.gradle של המודול של Android Auto. ב-Android Automotive OS, מוסיפים יחסי תלות ב-androidx.car.app:app-automotive לקובץ build.gradle של המודול של Android Automotive OS.

בנוסף, בקובץ AndroidManifest.xml צריך להצהיר על ההרשאות הרלוונטיות שנדרשות כדי לבקש את נתוני הרכב שבהם אתם רוצים להשתמש. חשוב לזכור שגם המשתמש צריך להעניק לכם את ההרשאות האלה. אתם יכולים להשתמש באותו קוד גם ב-Android Auto וגם ב-Android Automotive OS, במקום ליצור תהליכים שתלויים בפלטפורמה. עם זאת, ההרשאות הנדרשות שונות.

CarInfo

בטבלה הבאה מתוארים המאפיינים שמוצגים על ידי ממשקי ה-API של CarInfo וההרשאות שצריך לבקש כדי להשתמש בהם:

שיטות מאפיינים הרשאות של Android Auto הרשאות של Android Automotive OS נתמכת החל מרמת API של אפליקציה לרכב
fetchModel יצרן, דגם, שנת ייצור android.car.permission.CAR_INFO 3
fetchEnergyProfile סוגי מחברים של רכב חשמלי, סוגי דלק com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

הנתונים האלה זמינים רק בחלק מהרכבים עם Android Automotive OS שמריצים API מגרסה 30 ואילך

מידות חיצוניות לא רלוונטי android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
מצב כרטיס האגרה, סוג כרטיס האגרה 3
addEnergyLevelListener
removeEnergyLevelListener
רמת הטעינה של הסוללה, רמת הדלק, רמת הדלק נמוכה, הטווח שנותר com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_ENERGY,
android.car.permission.CAR_ENERGY_PORTS,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addSpeedListener
removeSpeedListener
מהירות גולמית, מהירות מוצגת (מוצגת במסך האשכולות של הרכב) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener
המרחק לפי מד המרחק com.google.android.gms.permission.CAR_MILEAGE הנתונים האלה לא זמינים ב-Android Automotive OS לאפליקציות שמותקנות מחנות Play. 3

לדוגמה, כדי לקבל את הטווח הנותר, יוצרים אובייקט CarInfo ואז יוצרים ומרשמים OnCarDataAvailableListener:

Kotlin

val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo

val listener = OnCarDataAvailableListener<EnergyLevel> { data ->
    if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) {
      val rangeRemaining = data.rangeRemainingMeters.value
    } else {
      // Handle error
    }
  }

carInfo.addEnergyLevelListener(carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener)

Java

CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo();

OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
  if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) {
    float rangeRemaining = data.getRangeRemainingMeters().getValue();
  } else {
    // Handle error
  }
};

carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener);
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener);

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

CarSensors

הכיתה CarSensors מעניקה גישה לנתוני המיקום, למד התאוצה, לג'ירוסקופ ולמצפן של הרכב. זמינות הערכים האלה עשויה להשתנות בהתאם ליצרן הציוד המקורי (OEM). הפורמט של הנתונים ממד התאוצה, מהג'ירוסקופ וממצפן זהה לפורמט של הנתונים שמתקבלים מ-SensorManager API. לדוגמה, כדי לבדוק את כיוון הרכב:

Kotlin

val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors

val listener = OnCarDataAvailableListener<Compass> { data ->
    if (data.orientations.status == CarValue.STATUS_SUCCESS) {
      val orientation = data.orientations.value
    } else {
      // Data not available, handle error
    }
  }

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener)

Java

CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors();

OnCarDataAvailableListener<Compass> listener = (data) -> {
  if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) {
    List<Float> orientations = data.getOrientations().getValue();
  } else {
    // Data not available, handle error
  }
};

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(),
    listener);
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener);

כדי לגשת לנתוני המיקום מהרכב, צריך גם להצהיר על ההרשאה android.permission.ACCESS_FINE_LOCATION ולבקש אותה.

בדיקה

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

מחזור החיים של CarAppService, ‏ Session ו-Screen

המחלקות Session ו-Screen מטמיעות את הממשק LifecycleOwner. כשהמשתמש מבצע פעולות באפליקציה, מתבצעת קריאה חוזרת (callback) למחזור החיים של האובייקטים Session ו-Screen, כפי שמתואר בתרשים הבא.

מחזור החיים של CarAppService ושל Session

איור 1. מחזור החיים של Session.

לקבלת פרטים מלאים, עיינו במסמכי העזרה של ה-method Session.getLifecycle.

מחזור החיים של מסך

איור 2. מחזור החיים של Screen.

פרטים מלאים זמינים במסמכי העזרה של השיטה Screen.getLifecycle.

הקלטה מהמיקרופון ברכב

באמצעות CarAppService של האפליקציה ו-API של CarAudioRecord, אפשר לתת לאפליקציה גישה למיקרופון ברכב של המשתמש. המשתמשים צריכים לתת לאפליקציה הרשאה לגשת למיקרופון ברכב. האפליקציה יכולה להקליט ולעבד את הקלט של המשתמש בתוך האפליקציה.

הרשאה להקלטה

לפני שמקליטים אודיו, צריך קודם להצהיר על ההרשאה להקלטה ב-AndroidManifest.xml ולבקש מהמשתמש להעניק אותה.

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

צריך לבקש את ההרשאה להקליט בזמן הריצה. בקטע בקשת הרשאות מוסבר איך לבקש הרשאה באפליקציה לרכב.

הקלטת אודיו

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

Kotlin

val carAudioRecord = CarAudioRecord.create(carContext)
        carAudioRecord.startRecording()

        val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE)
        while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording()
 

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        carAudioRecord.startRecording();

        byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE];
        while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording();
 

מיקוד אודיו

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

דוגמה לקבלת מוקד אודיו:

Kotlin

 
val carAudioRecord = CarAudioRecord.create(carContext)
        
        // Take audio focus so that user's media is not recorded
        val audioAttributes = AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            // Use the most appropriate usage type for your use case
            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
            .build()
        
        val audioFocusRequest =
            AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                .setAudioAttributes(audioAttributes)
                .setOnAudioFocusChangeListener { state: Int ->
                    if (state == AudioManager.AUDIOFOCUS_LOSS) {
                        // Stop recording if audio focus is lost
                        carAudioRecord.stopRecording()
                    }
                }
                .build()
        
        if (carContext.getSystemService(AudioManager::class.java)
                .requestAudioFocus(audioFocusRequest)
            != AudioManager.AUDIOFOCUS_REQUEST_GRANTED
        ) {
            // Don't record if the focus isn't granted
            return
        }
        
        carAudioRecord.startRecording()
        // Process the audio and abandon the AudioFocusRequest when done

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        // Take audio focus so that user's media is not recorded
        AudioAttributes audioAttributes =
                new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                        // Use the most appropriate usage type for your use case
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
                        .build();

        AudioFocusRequest audioFocusRequest =
                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                        .setAudioAttributes(audioAttributes)
                        .setOnAudioFocusChangeListener(state -> {
                            if (state == AudioManager.AUDIOFOCUS_LOSS) {
                                // Stop recording if audio focus is lost
                                carAudioRecord.stopRecording();
                            }
                        })
                        .build();

        if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest)
                != AUDIOFOCUS_REQUEST_GRANTED) {
            // Don't record if the focus isn't granted
            return;
        }

        carAudioRecord.startRecording();
        // Process the audio and abandon the AudioFocusRequest when done
 

ספריית בדיקות

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

בדוגמאות מפורטות דוגמאות לשימוש.

דיווח על בעיה בספריית האפליקציות של Android למכוניות

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

דיווח על בעיה חדשה

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