שימוש בספריית האפליקציות של 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().

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

<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
ניווט, נקודות עניין, מזג האוויר
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 של פונקציות ה-call back‏ onSurfaceAvailable() ו-onSurfaceDestroyed().

Kotlin

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

Java

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

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

המארח יכול לצייר רכיבי ממשק משתמש של התבניות מעל המפה. המארח מודיע על האזור של המשטח שבטוח שהוא ללא חסימות וחשוף במלואו למשתמש, על ידי קריאה לשיטה 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

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

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

כדי לקבל קריאות חזרה של אינטראקטיביות במפה, חובה להוסיף לחצן 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)

אפשר להפעיל את השיטה 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 מהסיבות הבאות:

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

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

בגדלים שונים של מסכים ברכב עשויות להופיע כמויות שונות של טקסט. בגרסה 2 ואילך של Car App API, אפשר לציין כמה וריאנטים של מחרוזת טקסט כדי להתאים אותה בצורה הטובה ביותר למסך. כדי לראות איפה אפשר להשתמש באפשרויות טקסט, מחפשים תבניות ורכיבים שמקבלים את הערך 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 מעניקה גישה לנתוני המיקום, למד התאוצה, לג'ירוסקופ ולמצפן של הרכב. זמינות הערכים האלה עשויה להשתנות בהתאם ליצרן הציוד המקורי. הפורמט של הנתונים ממד התאוצה, מהג'ירוסקופ וממצפן זהה לפורמט של הנתונים שמתקבלים מ-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.

פרטים מלאים זמינים במסמכי התיעוד של השיטה 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. חשוב למלא את כל המידע הנדרש בתבנית הדיווח על הבעיה.

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

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