שימוש בספריית האפליקציות של 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 לרכב מפורטים תיאורים של כל קטגוריה וקריטריונים להכללת אפליקציות בקטגוריות האלה.

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

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

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

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

יצירת מסך הפתיחה

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

הטמעה של ניווט במסך

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

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

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

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

ציור מפות

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

כדי להשתמש בתבניות הבאות, צריך להצהיר על אחת מההרשאות המתאימות באלמנט <uses-permission> בקובץ AndroidManifest.xml של האפליקציה.

תבנית הרשאה לתבנית הנחיות לגבי קטגוריות
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 של הקריאות החוזרות onSurfaceAvailable() ו-onSurfaceDestroyed().

Kotlin

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

Java

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

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

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

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

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

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

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

ציור מפות בלוח המחוונים

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

איך מאפשרים למשתמשים לבצע אינטראקציה עם המפה

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

תבנית האינטראקטיביות נתמכת מאז Car App API Level
NavigationTemplate 2
PlaceListNavigationTemplate (הוצא משימוש) 4
RoutePreviewNavigationTemplate (הוצא משימוש) 4
MapTemplate (הוצא משימוש) ‫5 (מבוא לתבנית)
MapWithContentTemplate ‫7 (introduction of template)

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

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

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

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

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

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

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

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

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

במצב הזזה, המארח של התבנית מתרגם קלט משתמש ממכשירי קלט שאינם מבוססי מגע, כמו בקרי חוגה ומשטחי מגע, לשיטות SurfaceCallback המתאימות. תגובה לפעולת המשתמש להפעלת מצב ההזזה או ליציאה ממנו באמצעות השיטה 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);
}

לפרטים נוספים על הפורמט של ACTION_NAVIGATE intent ועל דרכים נוספות להפעלת אפליקציות, אפשר לעיין בקטע הפעלת אפליקציה לרכב באמצעות 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();

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

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

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

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

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

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

איפוס פעולות

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

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

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

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);

ב-observer, אפשר להגיב לשינויים בסטטוס החיבור:

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

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

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();
  
  

Car Hardware APIs

החל מרמת Car App API 3, בספריית Car App יש ממשקי 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 נתמך מגרסת Car App 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

אזהרה: השם של ה-method ‏getOdometerMeters של המחלקה Mileage לא מדויק, והיא מחזירה קילומטרים ולא מטרים.

מרחק במד המרחק 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, אפשר לעיין בקטעים Sensors (חיישנים) ו-Sensor configuration (הגדרת חיישנים) במדריך Desktop Head Unit (יחידה ראשית למחשב). כדי לדמות נתוני חיישנים כשבודקים ב-Android Automotive OS, אפשר לעיין בקטע הדמיה של מצב החומרה במדריך לאמולטור של Android Automotive OS.

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

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

מחזור החיים של CarAppService ושל סשן

איור 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
 

Testing Library

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

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

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

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

יצירת בעיה חדשה

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